# Course: 5210 Communicating Data
# Purpose: Technical Appendix of MidTerm Project
# Author: Renato Albolea, Sakshi Madan 

# define default values for code chunks
knitr::opts_chunk$set(message = FALSE, dpi=300)

1 Loading Packages

# Clear environment
rm(list = ls(all = TRUE))

# Clear environmet of packages
if(is.null(sessionInfo()$otherPkgs) == FALSE)
  lapply(paste("package:", names(sessionInfo()$otherPkgs), sep=""),
         detach, character.only = TRUE, unload = TRUE)

# Load Packages
library(tidyverse) 
library(scales) #format numbers as currency
library(here) # easier way to find file path
library(kableExtra) #improved tables
library(gridExtra) # use to put graphs together in the same frame
library(ggthemes) #themes for graphs
library(GGally) #EDA Analyzis
library(qwraps2) #Nicer Summary
library(magrittr) #ables %<>%
library(janitor) #Tools for Examining and Cleaning Dirty Data
library(rcompanion) # to run pairwiseMedianTest function in the rcompanion package, which conducts Mood’s median test on all pairs of groups from one-way data
library(tools) # Apply toTitleCase function
# define the markup language we are working in.
library(colorspace)
library(plotly)
options(qwraps2_markup = "markdown")

2 Importing database

gm_data <- read_csv('/Users/sakshi/Documents/Syllabus/Data Visualization/Midterm Project/mtp_data.csv')
#gm_data <- read_csv(here('Midterm Project','mtp_data.csv'))

gm_data %>% head() %>% kable() %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = T)
UPC iri_key week units brand flavor package volume price promo ad
00-01-16000-11653 644347 1484 5 GENERAL MILLS CINNAMON TST CR CINNAMON TOAST BOX 0.06 0.5 0 A
00-01-16000-11653 248741 1483 2 GENERAL MILLS CINNAMON TST CR CINNAMON TOAST BOX 0.06 0.5 0 NONE
00-01-16000-11653 535806 1489 3 GENERAL MILLS CINNAMON TST CR CINNAMON TOAST BOX 0.06 0.5 0 NONE
00-01-16000-11945 675634 1489 2 GENERAL MILLS CHEERIOS TOASTED BOX 0.04 0.5 0 NONE
00-01-16000-11945 205272 1491 8 GENERAL MILLS CHEERIOS TOASTED BOX 0.04 0.5 0 NONE
00-01-16000-11945 248741 1492 5 GENERAL MILLS CHEERIOS TOASTED BOX 0.04 0.5 0 NONE
  • First observations:
    • The data is in a tidy format
    • There are 21,850 observations and 11 variables in the data set
  • Variables explanation
    • iri_key: store number (Need to be a Factor)
    • UPC: unique product number (Need to be a Factor)
    • week: week of sale
    • units: number of cereal packages sold
    • brand: producer and brand
    • promo: in store promotion 0/1 is no/yes (Need to be a Factor)
    • price: price per package
    • flavor: cereal flavor group (Need to be a Factor)
    • volume: cereal package size
    • package: type of cereal container (Need to be a Factor)
    • ad: (Need to be a Factor)
      NONE
      A – medium
      B – small

3 Adjusting data

gm_data %<>% mutate(iri_key = as.factor(iri_key),
                    UPC = as.factor(UPC),
                    promo = as.factor(promo),
                    flavor = as.factor(flavor),
                    ad = as.factor(ad),
                    package = as.factor(package)
                    )

# Separating in a bad way the brand and producer
gm_data %<>% separate(brand,"GENERAL MILLS ",into=c("aux", "flavor_GM"),remove = FALSE) %>% 
              separate(brand,"KELLOGGS ",into=c("aux", "flavor_KL"),remove = FALSE) %>% 
              separate(brand,"POST ",into=c("aux", "flavor_PT"),remove = FALSE) %>% 
              mutate(producer = case_when( not(is.na(flavor_GM)) ~ "General Mills",
                                           not(is.na(flavor_KL)) ~ "Kelloggs",
                                           not(is.na(flavor_PT)) ~ "POST",
                                           TRUE ~ "ERROR"
                                           ),
                     brand = case_when(producer=="General Mills" ~ toTitleCase(tolower(flavor_GM)),
                                      producer=="Kelloggs" ~ toTitleCase(tolower(flavor_KL)),
                                      producer=="POST" ~ toTitleCase(tolower(flavor_PT))
                                      ),
                     producer = as.factor(producer),
                     brand = as.factor(brand),
                     ad = factor(ad, 
                                 levels = c("A","B","NONE"), 
                                 labels = c("Medium","Small","None") 
                                  ),
                     revenue = units*price) %>% 
              select(-aux,-flavor_GM,-flavor_KL,-flavor_PT)

gm_data %>% str() 
## Classes 'tbl_df', 'tbl' and 'data.frame':    21850 obs. of  13 variables:
##  $ UPC     : Factor w/ 114 levels "00-01-16000-11653",..: 1 1 1 2 2 2 3 3 3 3 ...
##  $ iri_key : Factor w/ 1420 levels "200171","200197",..: 1041 446 1018 1217 48 446 1295 794 1184 1043 ...
##  $ week    : num  1484 1483 1489 1489 1491 ...
##  $ units   : num  5 2 3 2 8 5 6 1 4 14 ...
##  $ brand   : Factor w/ 15 levels "Cheerios","Cinnamon Tst Cr",..: 2 2 2 1 1 1 2 2 2 2 ...
##  $ flavor  : Factor w/ 5 levels "CINNAMON TOAST",..: 1 1 1 5 5 5 1 1 1 1 ...
##  $ package : Factor w/ 2 levels "BOX","CUP": 1 1 1 1 1 1 2 2 2 2 ...
##  $ volume  : num  0.06 0.06 0.06 0.04 0.04 0.04 0.12 0.12 0.12 0.12 ...
##  $ price   : num  0.5 0.5 0.5 0.5 0.5 0.5 1.09 1.59 1.59 1 ...
##  $ promo   : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ ad      : Factor w/ 3 levels "Medium","Small",..: 1 3 3 3 3 3 3 3 3 3 ...
##  $ producer: Factor w/ 3 levels "General Mills",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ revenue : num  2.5 1 1.5 1 4 2.5 6.54 1.59 6.36 14 ...

Question: Does Flavor give us any new information?

gm_data %>% group_by(brand,flavor) %>% summarise(n()) %>% kable() %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = T)
brand flavor n()
Cheerios REGULAR 4
Cheerios TOASTED 1454
Cinnamon Tst Cr CINNAMON TOAST 1834
Cocoa Krispies COCOA 881
Cocoa Puffs COCOA 1020
Froot Loops FRUIT 2192
Frosted Flakes REGULAR 2295
Frosted Mini Wheats REGULAR 1574
Grape Nuts REGULAR 1289
Kix REGULAR 1196
Lucky Charms REGULAR 3
Lucky Charms TOASTED 1678
Raisin Bran REGULAR 1266
Rice Krispies TOASTED 1450
Shredded Wheat REGULAR 1189
Smart Start TOASTED 1134
Special k TOASTED 1391
  • As we can see above, the only cases when flavor adds a new breakdown are:
    • Cheerios : Toasted and Regular
    • Lucky Charms: Toasted and Regular
    • However, the number of elements in Cheerios Regular (4) and Lucky Charms Regular (3) are very small, so we cannot have any statistical inference of difference among Cheerios Regular and Toasted or among Lucky Charms Regular and Toasted.

4 Base EDA

4.1 Step 1: Uni-variate non-graphical EDA

gm_data %>% summary()
##                 UPC           iri_key           week          units       
##  00-01-43000-10521:  676   656444 :   35   Min.   :1479   Min.   : 1.000  
##  00-01-38000-01621:  666   256951 :   31   1st Qu.:1492   1st Qu.: 3.000  
##  00-01-38000-00828:  660   259661 :   31   Median :1505   Median : 7.000  
##  00-01-16000-27569:  639   267403 :   31   Mean   :1505   Mean   : 8.579  
##  00-02-38000-66330:  618   652139 :   31   3rd Qu.:1518   3rd Qu.:12.000  
##  00-01-38000-01611:  612   681735 :   31   Max.   :1530   Max.   :28.000  
##  (Other)          :17979   (Other):21660                                  
##                  brand                  flavor     package    
##  Frosted Flakes     : 2295   CINNAMON TOAST:1834   BOX:21306  
##  Froot Loops        : 2192   COCOA         :1901   CUP:  544  
##  Cinnamon Tst Cr    : 1834   FRUIT         :2192              
##  Lucky Charms       : 1681   REGULAR       :8816              
##  Frosted Mini Wheats: 1574   TOASTED       :7107              
##  Cheerios           : 1458                                    
##  (Other)            :10816                                    
##      volume          price       promo          ad       
##  Min.   :0.040   Min.   :0.250   0:17305   Medium: 1456  
##  1st Qu.:0.750   1st Qu.:3.190   1: 4545   Small : 1061  
##  Median :1.060   Median :3.790             None  :19333  
##  Mean   :1.016   Mean   :3.763                           
##  3rd Qu.:1.120   3rd Qu.:4.390                           
##  Max.   :4.000   Max.   :9.990                           
##                                                          
##           producer        revenue      
##  General Mills: 7189   Min.   :  0.48  
##  Kelloggs     :12183   1st Qu.: 11.80  
##  POST         : 2478   Median : 24.50  
##                        Mean   : 31.01  
##                        3rd Qu.: 44.09  
##                        Max.   :155.48  
## 
  • Findings
    • Units seem to be skewed to the right, so we should use median instead of mean;
    • Cereals are bought in Cups only 2.5% of the time and in Boxes on the rest of the time;
    • Volume seems not skewed, thus we can use mean or median;
    • Price seems not skewed, thus we can use mean or median;
    • Cereals are in promo 20.8% of the time
    • Cereals have small ads 4.9% of the time and medium ads 6.7% of the time;
    • General Mills represents 32.9% of the sales while Kelloggs represents 55.8% and Post represents 11.3%;

4.2 Step 2: Univariate graphical analysis

  • histograms and boxplots of quantitative variables
  • uni-variate bar graphs of factor variables

4.2.1 week

# Uni-variate graphical analysis of quantiative variables
grid.arrange(gm_data %>% ggplot(mapping = aes(x = week)) + geom_histogram(),
             gm_data %>% ggplot(mapping = aes(x = 1, y = week)) + geom_boxplot() + coord_flip(),
             ncol = 1)

- The weekly sale seems to have a pattern that could be explored in the future.

4.2.2 units

# Uni-variate graphical analysis of quantiative variables
grid.arrange(gm_data %>% ggplot(mapping = aes(x = units)) + geom_histogram(bins = max(gm_data$units)),
             gm_data %>% ggplot(mapping = aes(x = 1, y = units)) + geom_boxplot() + coord_flip(),
             ncol = 1)

- The number of units sold per transaction varies and follows a log normal distribution as expected.
- It would be interesting to understand better the relationship among number of units, volume, promotion, and ads for larger amounts of units in a following project.

4.2.3 volume

# Uni-variate graphical analysis of quantiative variables
grid.arrange(gm_data %>% ggplot(mapping = aes(x = volume)) + geom_histogram(),
             gm_data %>% ggplot(mapping = aes(x = 1, y = volume)) + geom_boxplot() + coord_flip(),
             ncol = 1)

- Cereals are sold in 7 main sizes.

4.2.4 Price

# Uni-variate graphical analysis of quantiative variables
grid.arrange(gm_data %>% ggplot(mapping = aes(x = price)) + geom_histogram(),
             gm_data %>% ggplot(mapping = aes(x = 1, y = price)) + geom_boxplot() + coord_flip(),
             ncol = 1)

- Price seems to be skewed, thus we should use median instead of mean.

4.2.5 Revenue

# Uni-variate graphical analysis of quantiative variables
grid.arrange(gm_data %>% ggplot(mapping = aes(x = revenue)) + geom_histogram(),
             gm_data %>% ggplot(mapping = aes(x = 1, y = revenue)) + geom_boxplot() + coord_flip(),
             ncol = 1)

- Revenue seems to be skewed, thus we should use median instead of mean.

4.2.6 iri_key(stores)

# Uni-variate graphical analysis of factor variables
gm_data %>% ggplot(mapping = aes(x = iri_key)) + geom_bar()

4.2.7 flavor

# Uni-variate graphical analysis of factor variables
gm_data %>% ggplot(mapping = aes(x = flavor)) + geom_bar()

- Regular and Toasted are the main flavors.

4.2.8 package

# Uni-variate graphical analysis of factor variables
gm_data %>% ggplot(mapping = aes(x = package)) + geom_bar()

- Consumers buy almost only boxes.

4.2.9 promo

# Uni-variate graphical analysis of factor variables
gm_data %>% ggplot(mapping = aes(x = promo)) + geom_bar()

- Promo periods seems to be a good size of the data.

4.2.11 producer

# Uni-variate graphical analysis of factor variables
gm_data %>% ggplot(mapping = aes(x = producer)) + geom_bar()

- Kelloggs is the main brand.

4.2.12 name

# Uni-variate graphical analysis of factor variables
gm_data %>% ggplot(mapping = aes(x = brand)) + geom_bar()

4.2.13 Findings

  • Sales varies between the weeks, but it appears to follow a pattern with some expressive declines (why?)
    • Consumer tend to buy small units of products and the distribution seems logarithmic (why there are some gaps in units? what are the values?)
    • The volume near 1 is more usually bought than the others. (which size? does price/market share varies with volume?)
    • Regular and Toasted are the favorite flavors 72.9% of sales

4.2.14 Questions

  • Does sales / price varies with Ad?
    • Does sales / price varies with promo?
    • Does promo/ad impacts market share?
    • Does promo/ad impacts market volume / units?
    • Does promo/ad and week volume has correlation? is anyway linked with the drop in sales over some weeks?
    • What is the relationship between volume and price?
    • Does the brands use the same price strategy?
    • What are the most expensive / cheapest cereals? Is that constant over weeks?

4.3 Step 3: Multi-variate non-graphical analysis

4.3.1 Promotion x Producer

# Proportion contingency/cross table
gm_data %>% 
  tabyl(producer, promo) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns() %>%
  kable()%>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F)
producer 0 1 Total
General Mills 82.2% (5909) 17.8% (1280) 100.0% (7189)
Kelloggs 77.7% (9470) 22.3% (2713) 100.0% (12183)
POST 77.7% (1926) 22.3% (552) 100.0% (2478)
Total 79.2% (17305) 20.8% (4545) 100.0% (21850)

4.3.2 Ad x Producer

# Proportion contingency/cross table
gm_data %>% 
  tabyl(producer, ad) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns() %>%
  kable()%>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F)
producer Medium Small None Total
General Mills 6.1% (442) 3.8% (272) 90.1% (6475) 100.0% (7189)
Kelloggs 7.4% (903) 5.5% (664) 87.1% (10616) 100.0% (12183)
POST 4.5% (111) 5.0% (125) 90.5% (2242) 100.0% (2478)
Total 6.7% (1456) 4.9% (1061) 88.5% (19333) 100.0% (21850)

4.3.3 Flavor x Producer

# Proportion contingency/cross table
gm_data %>% 
  tabyl(producer, flavor) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns() %>%
  kable()%>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = T)
producer CINNAMON TOAST COCOA FRUIT REGULAR TOASTED Total
General Mills 25.5% (1834) 14.2% (1020) 0.0% (0) 16.7% (1203) 43.6% (3132) 100.0% (7189)
Kelloggs 0.0% (0) 7.2% (881) 18.0% (2192) 42.1% (5135) 32.6% (3975) 100.0% (12183)
POST 0.0% (0) 0.0% (0) 0.0% (0) 100.0% (2478) 0.0% (0) 100.0% (2478)
Total 8.4% (1834) 8.7% (1901) 10.0% (2192) 40.3% (8816) 32.5% (7107) 100.0% (21850)

4.3.4 Flavor x Promo

# Proportion contingency/cross table
gm_data %>% 
  tabyl(flavor, promo) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns() %>%
  kable()%>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F)
flavor 0 1 Total
CINNAMON TOAST 83.8% (1537) 16.2% (297) 100.0% (1834)
COCOA 74.5% (1416) 25.5% (485) 100.0% (1901)
FRUIT 76.3% (1673) 23.7% (519) 100.0% (2192)
REGULAR 80.0% (7057) 20.0% (1759) 100.0% (8816)
TOASTED 79.1% (5622) 20.9% (1485) 100.0% (7107)
Total 79.2% (17305) 20.8% (4545) 100.0% (21850)

4.3.5 Flavor x ad

# Proportion contingency/cross table
gm_data %>% 
  tabyl(flavor, ad) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns() %>%
  kable()%>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F)
flavor Medium Small None Total
CINNAMON TOAST 6.2% (114) 2.8% (51) 91.0% (1669) 100.0% (1834)
COCOA 6.7% (128) 4.8% (91) 88.5% (1682) 100.0% (1901)
FRUIT 7.0% (153) 4.9% (108) 88.1% (1931) 100.0% (2192)
REGULAR 5.9% (516) 5.0% (439) 89.2% (7861) 100.0% (8816)
TOASTED 7.7% (545) 5.2% (372) 87.1% (6190) 100.0% (7107)
Total 6.7% (1456) 4.9% (1061) 88.5% (19333) 100.0% (21850)

4.3.6 Promo x ad

# Proportion contingency/cross table
gm_data %>% 
  tabyl(promo, ad) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns() %>%
  kable()
promo Medium Small None Total
0 3.9% (683) 2.3% (402) 93.7% (16220) 100.0% (17305)
1 17.0% (773) 14.5% (659) 68.5% (3113) 100.0% (4545)
Total 6.7% (1456) 4.9% (1061) 88.5% (19333) 100.0% (21850)

4.3.7 Producer x Flavor x Promo

# Proportion contingency/cross table
gm_data %>% 
  tabyl(producer, flavor, promo) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns()
## $`0`
##       producer CINNAMON TOAST        COCOA        FRUIT       REGULAR
##  General Mills   26.0% (1537) 13.0%  (769)  0.0%    (0)  17.0% (1004)
##       Kelloggs    0.0%    (0)  6.8%  (647) 17.7% (1673)  43.6% (4127)
##           POST    0.0%    (0)  0.0%    (0)  0.0%    (0) 100.0% (1926)
##          Total    8.9% (1537)  8.2% (1416)  9.7% (1673)  40.8% (7057)
##       TOASTED          Total
##  44.0% (2599) 100.0%  (5909)
##  31.9% (3023) 100.0%  (9470)
##   0.0%    (0) 100.0%  (1926)
##  32.5% (5622) 100.0% (17305)
## 
## $`1`
##       producer CINNAMON TOAST       COCOA       FRUIT       REGULAR
##  General Mills    23.2% (297) 19.6% (251)  0.0%   (0)  15.5%  (199)
##       Kelloggs     0.0%   (0)  8.6% (234) 19.1% (519)  37.2% (1008)
##           POST     0.0%   (0)  0.0%   (0)  0.0%   (0) 100.0%  (552)
##          Total     6.5% (297) 10.7% (485) 11.4% (519)  38.7% (1759)
##       TOASTED         Total
##  41.6%  (533) 100.0% (1280)
##  35.1%  (952) 100.0% (2713)
##   0.0%    (0) 100.0%  (552)
##  32.7% (1485) 100.0% (4545)

4.3.8 Producer x Flavor x ad

# Proportion contingency/cross table
gm_data %>% 
  tabyl(producer, flavor, ad) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages("row") %>%
  adorn_pct_formatting() %>%
  adorn_ns() 
## $Medium
##       producer CINNAMON TOAST       COCOA       FRUIT      REGULAR
##  General Mills    25.8% (114) 17.2%  (76)  0.0%   (0)  15.8%  (70)
##       Kelloggs     0.0%   (0)  5.8%  (52) 16.9% (153)  37.1% (335)
##           POST     0.0%   (0)  0.0%   (0)  0.0%   (0) 100.0% (111)
##          Total     7.8% (114)  8.8% (128) 10.5% (153)  35.4% (516)
##      TOASTED         Total
##  41.2% (182) 100.0%  (442)
##  40.2% (363) 100.0%  (903)
##   0.0%   (0) 100.0%  (111)
##  37.4% (545) 100.0% (1456)
## 
## $Small
##       producer CINNAMON TOAST      COCOA       FRUIT      REGULAR
##  General Mills     18.8% (51) 19.9% (54)  0.0%   (0)  20.2%  (55)
##       Kelloggs      0.0%  (0)  5.6% (37) 16.3% (108)  39.0% (259)
##           POST      0.0%  (0)  0.0%  (0)  0.0%   (0) 100.0% (125)
##          Total      4.8% (51)  8.6% (91) 10.2% (108)  41.4% (439)
##      TOASTED         Total
##  41.2% (112) 100.0%  (272)
##  39.2% (260) 100.0%  (664)
##   0.0%   (0) 100.0%  (125)
##  35.1% (372) 100.0% (1061)
## 
## $None
##       producer CINNAMON TOAST        COCOA        FRUIT       REGULAR
##  General Mills   25.8% (1669) 13.7%  (890)  0.0%    (0)  16.6% (1078)
##       Kelloggs    0.0%    (0)  7.5%  (792) 18.2% (1931)  42.8% (4541)
##           POST    0.0%    (0)  0.0%    (0)  0.0%    (0) 100.0% (2242)
##          Total    8.6% (1669)  8.7% (1682) 10.0% (1931)  40.7% (7861)
##       TOASTED          Total
##  43.8% (2838) 100.0%  (6475)
##  31.6% (3352) 100.0% (10616)
##   0.0%    (0) 100.0%  (2242)
##  32.0% (6190) 100.0% (19333)

4.3.9 Correlation Table

gm_data %>% select(week, units, price, volume) %>% cor() 
##               week       units       price      volume
## week    1.00000000 -0.03405928  0.02839918 -0.01999516
## units  -0.03405928  1.00000000 -0.19070782  0.02371249
## price   0.02839918 -0.19070782  1.00000000  0.54332999
## volume -0.01999516  0.02371249  0.54332999  1.00000000
  • Findings
    • The use of promotion seems similar among the producer
    • General Mills uses less advertisement than Kelloggs
    • Post only sell Regular Flavor while General Mills and Kelloggs are more diversified
    • There is a heavier use of advertisement when the products are in promotion.
    • The sales proportions of General Mills does not depend on Ads.
    • Cocoa and Fruit flavors use more promotion than Regular and Toasted flavors.
    • Cinnamon Toast is the flavor with less promo and less ads.
    • There is a week correlation (0.54333) between volume and price. We need to verify if it is statistical significant.

    • During promotions General Mills sell less Cinnamon Toasted, Regular, and Toasted Flavors
    • During promotions Kelloggs also sell less Regular, and Toasted Flavors
    • During promotions General Mills and Kelloggs sell more Cocoa and Fruit flavors

4.4 Step 4: Multi-variate graphical analysis

4.4.1 Price vs Producer

gm_data %>% ggplot(aes(x=producer,y=price))+geom_boxplot()

gm_data %>% ggplot(aes(x=price,fill=producer))+geom_density(alpha=0.5)

  • All the producers have similar median price
  • POST has narrower interquartile price as compared to other two

4.4.2 Price vs Flavor

gm_data %>% ggplot(aes(x=flavor,y=price))+geom_boxplot()

med_price = gm_data %>% group_by(flavor) %>% summarise(med_price = median(price))
  • Cinnamon Toast and Toasted have a slightly higher median price (3.99) as compared to others and also a larger interquartile range
  • Cocoa has least median price (3.49) as compared to other flavors

4.4.3 Price vs Flavor vs Producer

gm_data %>% ggplot(aes(x=flavor,y=price, color=producer))+geom_boxplot()

  • For cocoa flavor, Kelloggs has a higher median price than General Mills
  • For regular flavor, Kelloggs again has a higher median price than General Mills and Post
  • For toasted flavor, median price is similar for both Kelloggs and General Mills

4.4.4 Price vs volume

gm_data %>% ggplot(aes(x=volume,y=price, color=producer))+geom_point() + geom_smooth(mapping = aes(color = producer), method = "lm", se = FALSE)

gm_data %>% ggplot(aes(x=volume,y=price/volume, color=producer))+geom_point() + geom_smooth(mapping = aes(color = producer), method = "loess", se = FALSE)

  • Price per volume decreases as with the increase in volume across all three producers

4.4.5 Relationship among the variables

#gm_data %>% select(-week,-iri_key, -UPC) %>%  ggpairs()

gm_data %>% select(units,flavor,promo,ad,producer,price) %>%  ggpairs()

  • After looking at it, we noticed that units and price vary across promos and ads in different directions, we created a new variable revenue = units * price to account for both impacts at the same time

4.4.6 Revenue vs Producer

gm_data %>% ggplot(aes(x=producer,y = revenue)) + geom_boxplot()

  • General Mills appear to have a higher revenue followed by Kelloggs and Post

4.4.7 Revenue vs Producer vs Promo

gm_data %>% ggplot(aes(x=producer,y = revenue, color=promo)) + geom_boxplot()

gm_data %>% ggplot(aes(x=producer,y = units, color=promo)) + geom_boxplot()

gm_data %>% ggplot(aes(x=producer,y = price, color=promo)) + geom_boxplot()

  • Across all three producers, median revenue increases with promo
  • With promo, Kelloggs has a higher rate of median revenue increase as compared to other two
  • With promo, Kelloggs also has an increase on the upper portion of its revenue interquartile range
  • As mentioned before, the variation in number of units and price is very strong when producers use promo but in opposite direction, so it becomes very difficult to conclude the efficiency without using the revenue variable

4.4.8 Revenue vs Producer vs ads

gm_data %>% ggplot(aes(x=producer,y = revenue, color=ad)) + geom_boxplot()

gm_data %>% ggplot(aes(x=producer,y = units, color=ad)) + geom_boxplot()

gm_data %>% ggplot(aes(x=producer,y = price, color=ad)) + geom_boxplot()

  • The median revenue of General Mills is slightly higher with medium ads
  • The median revenue of Kelloggs is slightly higher with small ads
  • Small ads also generate more revenue for Post

4.4.9 Median revenue vs Producer vs ads

med_ads_prod <- gm_data %>% group_by(producer,ad) %>% summarise(med_revenue = median(revenue),
                                                                   n = n())
med_ads_prod %>% ggplot(aes(x = producer, y = med_revenue, fill = ad)) + geom_bar(stat = 'identity', position = 'dodge') + coord_flip()

4.4.10 Median revenue vs Producer vs promo

med_promo_prod <- gm_data %>% group_by(producer,promo) %>% summarise(med_revenue = median(revenue),
                                                                   n = n())
med_promo_prod %>% ggplot(aes(x = producer, y = med_revenue, fill = promo)) + geom_bar(stat = 'identity', position = 'dodge') + coord_flip()

4.4.11 Median revenue vs flavor vs ads

med_ads_flavor <- gm_data %>% group_by(flavor,ad) %>% summarise(med_revenue = median(revenue),
                                                                   n = n())
med_ads_flavor %>% ggplot(aes(x = flavor, y = med_revenue, fill = ad)) + geom_bar(stat = 'identity', position = 'dodge') + coord_flip()

ggsave('revenue_flavor_ads.png')
  • The median revenue for a flavor reduces as the length of the ad increases except for Toasted and Cinnamon Toast

4.4.12 Median revenue vs flavor vs promo

med_promo_flavor <- gm_data %>% group_by(flavor,promo) %>% summarise(med_revenue = median(revenue),
                                                                   n = n())
med_promo_flavor %>% ggplot(aes(x = flavor, y = med_revenue, fill = promo)) + geom_bar(stat = 'identity', position = 'dodge') + coord_flip()

  • The median revenue for all the flavors increase with promo

4.4.13 Median revenue vs brand vs ads

med_ads_brand <- gm_data %>% group_by(producer, brand, ad) %>% summarise(med_revenue = median(revenue),
                                                                   n = n())
med_ads_brand %>% ggplot(aes(x = paste(producer,brand), y = med_revenue, fill = ad)) + geom_bar(stat = 'identity', position = 'dodge') + coord_flip()

  • Interestingly, Cheerios from General Mills lose median revenue with ads
  • Also, Frosted mini wheats from Kelloggs is losing median revenue with ads
  • Kelloggs Raisin Bran has a higher median revenue with medium ads

4.4.14 Median revenue vs brand vs promo

med_promo_brand <- gm_data %>% group_by(producer, brand,promo) %>% summarise(med_revenue = median(revenue),
                                                                   n = n())
med_promo_brand %>% ggplot(aes(x = paste(producer,brand), y = med_revenue, fill = promo)) + geom_bar(stat = 'identity', position = 'dodge') + coord_flip()

ggsave('revenue_brand_promo.png')
  • Interestingly, Cheerios from General Mills lose median revenue with promo

4.4.15 Median revenue vs producer vs promo and Ads

med_ads_promo_producer <- gm_data %>% group_by(producer, ad, promo) %>% summarise(med_revenue = median(revenue), 
                                                                                  n = n())
med_ads_promo_producer %>% ggplot(aes(x = producer, y = med_revenue, fill = paste(promo,ad))) + 
  geom_bar(stat = 'identity', position = 'dodge') + coord_flip()

gm_data %>% ggplot(aes(x = producer, y = revenue,color=paste(promo,ad))) + 
  geom_boxplot() + coord_flip()

  • General Mills make most median revenue on medium ads with promo followed by small ads without promo
  • Kelloggs make most median revenue on small ads with promo followed by medium ads with promo

4.4.16 Volume distribution

gm_data %>% ggplot(aes(x=volume)) + geom_density()+facet_wrap( ~ producer)

gm_data %>% ggplot(aes(x=volume,fill=ad)) + geom_density(alpha=0.4)+facet_grid(rows= vars(producer))

  • For Post, during ad periods, there is a concentration of volumes in 4 different sizes

4.4.17 Units distribution

gm_data %>% ggplot(aes(x=units)) + geom_density()+facet_wrap( ~ producer)

gm_data %>% ggplot(aes(x=units,fill=ad)) + geom_density(alpha=0.4)+facet_grid(rows= vars(producer))

  • When we have ads, people tend to buy more units than in no ads periods.

  • In summary:
    1. General Mills seems to have a higher revenue during a Promo period with Medium size ads;
    2. Kelloggs appears to do better in a Promo with small size ads strategy;
    3. The data suggests that Post do better during promo and small size strategy;
    4. It is interesting to notice that General Mills Cheerios has a lower median revenue during promo and ads. The same appears to be true for Kelloggs Frosted Mini Wheats;
    5. Fruits and Cocoa flavor show signs that have the highest impact on revenue among the flavors during small ad campaigns;

5 Testing statistical significance

5.1 General Mills has higher revenue in promo periods with medium size ads

As we saw earlier in this document since revenue is a skewed measure we should use the median instead of the mean. To test if the medians are different we will utilize the Mood’s median test.

median_test <- gm_data %>% filter(producer == "General Mills") %>% mutate(promo_ad = paste(promo,ad),
                                                                          promo_ad = as.factor(promo_ad))

PT = pairwiseMedianMatrix(revenue ~ promo_ad,
                        data   = median_test,
                        exact  = NULL,
                        method = "bonferroni")
kable(PT$Adjusted) %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = T)
0 Medium 0 None 0 Small 1 Medium 1 None 1 Small
0 Medium 1 1.000000 1 1.000000 1.000000 1
0 None 1 1.000000 1 0.003402 0.007353 1
0 Small 1 1.000000 1 1.000000 1.000000 1
1 Medium 1 0.003402 1 1.000000 0.725200 1
1 None 1 0.007353 1 0.725200 1.000000 1
1 Small 1 1.000000 1 1.000000 1.000000 1

Thus, we can conclude that General Mills’ median revenue during promo and medium ads (median revenue =$38.25 ) is statistically different from no promo with no ads (median revenue = $28.74, p-value adjusted = 0.3402%) at 95% confidence level, and that General Mills’ median revenue, during promo and no ads(median revenue = $32.32), is also different from no promo and no ads (p-value adjusted = 0.7353%) at 95% confidence level.

According to the data set, when General Mills uses promo and medium ads, the median revenue increases around 33.1% in comparison to the base case scenario(no promo and no ads). Similarly, with promo and no ads the median revenue increases around 12.5% in comparison to the base case scenario.

Although the median revenue from the scenario of promo and medium ads is not statistically different from the case with promo and no ads (p-value adjusted = 100% ) the increase of revenue is practically significant (increase of 18.3%), thus we conclude that doing a medium ad campaign is important and should be considered to be used with promo even though the increase in revenue is not reliable.

med_ads_promo_GM <- gm_data %>% filter(producer == "General Mills") %>% group_by(ad, promo) %>% summarise(med_revenue = median(revenue), 
                                                                                                          n = n()) %>% ungroup()
sub_title = paste0("In that scenario median revenue increases ",
                   round(med_ads_promo_producer$med_revenue[2]/med_ads_promo_producer$med_revenue[5]-1,3)*100,
                   "% as compared \nto the base case of no Promo and no Ads")
          
med_ads_promo_GM %<>% mutate(scenario = as.factor(paste(promo,ad)),
                         scenario = factor(scenario, 
                                      levels = c("0 None","0 Small","0 Medium", "1 None","1 Small", "1 Medium"), 
                                      labels = c("No Promo, No Ad","No Promo, Small Ad","No Promo, Medium Ad",
                                                 "With Promo, No Ad","With Promo, Small Ad","With Promo, Medium Ad") 
                                        )
                         )
med_ads_promo_GM %>% ggplot(aes(x = reorder(scenario,-as.numeric(scenario)), y = med_revenue, fill = scenario)) + 
  geom_bar(stat = 'identity', position = 'dodge') + coord_flip() +
  scale_y_continuous(labels = dollar) +
  ggtitle("The highest median revenue for General Mills
is when using promo and medium ads",
          sub = sub_title) +
  labs(x = "", y = "Median Revenue") +
  theme_economist_white(gray_bg =FALSE) + scale_colour_economist()+
  scale_fill_manual(values=c("grey65", "grey65", "grey65", "grey65","grey65","grey25"))+
  theme(legend.position = "none")

ggsave(filename = "General_Mills.png")

5.2 General Mills Cheerios has a lower median revenue during promo and ads

median_test_che <- gm_data %>% filter(brand == "Cheerios") %>% mutate(promo_ad = paste(promo,ad),
                                                                  promo_ad = as.factor(promo_ad))

PT = pairwiseMedianMatrix(revenue ~ promo_ad,
                        data   = median_test_che,
                        exact  = NULL,
                        method = "bonferroni")

kable(PT$Adjusted) %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = T)
0 Medium 0 None 0 Small 1 Medium 1 None 1 Small
0 Medium 1 1.0000 1 1 1.0000 1
0 None 1 1.0000 1 1 0.1233 1
0 Small 1 1.0000 1 1 1.0000 1
1 Medium 1 1.0000 1 1 1.0000 1
1 None 1 0.1233 1 1 1.0000 1
1 Small 1 1.0000 1 1 1.0000 1
gm_data_che <- gm_data %>% filter(brand == "Cheerios") %>% mutate(promo_ad = paste(promo,ad)) %>% group_by(promo_ad) %>% summarise(med_rev = median(revenue))

For the null hypothesis of Cheerios median revenue under promo and no ads being equal to median revenue under no promo and no ads, we don’t have a strong enough evidence (p-value = 0.1233) to reject it at a confidence level of 95%. However, at a confidence level of 85%, we have sufficient evidence to reject the null hypothesis.

According to the data, Cheerios’ median revenue is 11.6% lower when in promo and no ads as compared to when not in promo and no ads, thus we conclude that General Mills should not use promos for Cheerios.

sub_title1 = paste0("The median revenue of Cheerios declines by ",-round(gm_data_che$med_rev[5] / gm_data_che$med_rev[2] - 1,3)*100,
                   "% when in promotion")

med_che_promo <- gm_data %>% filter(producer == "General Mills") %>% group_by(brand,promo) %>% summarise(med_revenue = median(revenue), n = n())

med_che_promo %>% ggplot(aes(x = reorder(brand,-med_revenue), y = med_revenue, fill = factor(promo, levels = rev(levels(promo))))) + geom_bar(stat = 'identity',  position = "dodge") + 
  coord_flip() +
  labs(x = "", y = "Median Revenue", title = "Cheerios is the only General Mills brand whose \nmedian revenue decreases with promotion",subtitle = sub_title1) + 
  scale_y_continuous(labels = dollar) +
  scale_fill_manual(values = c('grey75','grey25'), name = "", labels = c("With Promo","Without Promo")) +
  theme_economist_white(gray_bg =FALSE) + scale_colour_economist()+
  theme(axis.ticks.y = element_blank(), legend.position = "right", legend.text = element_text(size = 9)) +
  guides(fill = guide_legend(reverse = TRUE))

ggsave(filename = "Cheerios.png")
#Beginning
comparison_chart <- gm_data %>% group_by(producer, week) %>% summarise(total_rev = sum(revenue)) %>% ggplot(mapping = aes(x = week, y = total_rev, color = producer)) +
  geom_line() +
  labs(title = 'Total revenue generated by Kelloggs is much higher than General Mills', x = 'Week', y = 'Total Revenue', subtitle = '') +
  theme_economist_white(gray_bg =FALSE) +
  #theme_classic() +
  theme(legend.position = 'FALSE') + 
  scale_color_manual(values = c('grey 25','dark red','light grey')) +
  #scale_color_discrete_qualitative(palette = 'Warm') +
  annotate("text", x = 1504, y = 8000, label = "Kelloggs", color = 'dark red') +
  annotate("text", x = 1510, y = 5700, label = "General Mills", color = 'grey 25') +
  annotate("text", x = 1510, y = 1700, label = "Post", color = 'dark grey') +
  scale_y_continuous(labels = dollar)
comparison_chart

ggsave('comparison_chart.png',plot = comparison_chart)

#Middle1
med_ads_promo_GM <- gm_data %>% filter(producer == "General Mills") %>% group_by(ad, promo) %>% summarise(med_revenue = median(revenue),n =n()) %>% ungroup()
med_ads_promo_GM %<>% mutate(scenario = as.factor(paste(promo,ad)),scenario = factor(scenario,levels = c("0 None","0 Small","0 Medium", "1 None","1 Small", "1 Medium"),labels = c("No Promo, No Ad","No Promo, Small Ad","No Promo, Medium Ad","With Promo, No Ad","With Promo, Small Ad","With Promo, Medium Ad")))
general_mills_chart <- med_ads_promo_GM %>% ggplot(aes(x = reorder(scenario,-as.numeric(scenario)), y = med_revenue, fill = scenario)) + 
  geom_bar(stat = 'identity', position = 'dodge') + 
  coord_flip() +
  scale_y_continuous(labels = dollar) +
  ggtitle("The highest median revenue for General Mills is when using promo and medium ads", sub = 'sub_title') +
  labs(x = "", y = "Median Revenue") +
  theme_classic() +
  #theme_economist_white(gray_bg =FALSE) + scale_colour_economist()+
  scale_fill_manual(values=c("grey65", "grey65", "grey65", "grey65","grey65","grey25"))+
  theme(legend.position = "none")

ggsave('general_mills_chart.png',plot = general_mills_chart)

#Middle2
med_che_promo <- gm_data %>% filter(producer == "General Mills") %>% group_by(brand,promo) %>% summarise(med_revenue = median(revenue), n = n())
cheerios_promo_chart <- med_che_promo %>% ggplot(aes(x = reorder(brand,-med_revenue), y = med_revenue, fill = factor(promo, levels = rev(levels(promo))))) + geom_bar(stat = 'identity',  position = "dodge") + 
  coord_flip() +
  labs(x = "", y = "Median Revenue", title = "Cheerios is the only General Mills brand whose \nmedian revenue decreases with promotion",subtitle = sub_title1) + 
  scale_y_continuous(labels = dollar) +
  scale_fill_manual(values = c('grey75','grey25'), name = "", labels = c("With Promo","Without Promo")) +
  theme_classic() +
  #theme_economist_white(gray_bg =FALSE) + scale_colour_economist()+
  theme(axis.ticks.y = element_blank(), legend.position = "right", legend.text = element_text(size = 9)) +
  guides(fill = guide_legend(reverse = TRUE))
ggsave('cheerios_promo_chart.png',plot = cheerios_promo_chart)

#Middle3
med_che_ad <- gm_data %>% filter(producer == "General Mills") %>% group_by(brand,ad) %>% summarise(med_revenue = median(revenue), n = n())
cheerios_ad_chart <- med_che_ad %>% ggplot(aes(x = reorder(brand,-med_revenue), y = med_revenue, fill = ad)) + geom_bar(stat = 'identity',  position = "dodge") + 
  coord_flip() +
  labs(x = "", y = "Median Revenue", title = "Cheerios is the only General Mills brand whose \nmedian revenue decreases with ads",subtitle = sub_title1) + 
  scale_y_continuous(labels = dollar) +
  #scale_fill_manual(values = c('grey75','grey25'), name = "", labels = c("With Promo","Without Promo")) +
  theme_classic() +
  #theme_economist_white(gray_bg =FALSE) + scale_colour_economist()+
  theme(axis.ticks.y = element_blank(), legend.position = "right", legend.text = element_text(size = 9)) +
  guides(fill = guide_legend(reverse = TRUE))
ggsave('cheerios_ad_chart.png',plot = cheerios_ad_chart)
LS0tCnRpdGxlOiAiTWlkIFRlcm0gUHJvamVjdCIKZGVzY3JpcHRpb246IHwKICAgIFRlY2huaWNhbCBBcHBlbmRpeC4KYXV0aG9yOgogIC0gbmFtZTogUmVuYXRvIEFsYm9sZWEgCiAgLSBuYW1lOiBTYWtzaGkgTWFkYW4gCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OiAKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgZGZfcHJpbnQ6IGthYmxlCiAgICBrZWVwX21kOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtyIG9wdGlvbnN9CiMgQ291cnNlOiA1MjEwIENvbW11bmljYXRpbmcgRGF0YQojIFB1cnBvc2U6IFRlY2huaWNhbCBBcHBlbmRpeCBvZiBNaWRUZXJtIFByb2plY3QKIyBBdXRob3I6IFJlbmF0byBBbGJvbGVhLCBTYWtzaGkgTWFkYW4gCgojIGRlZmluZSBkZWZhdWx0IHZhbHVlcyBmb3IgY29kZSBjaHVua3MKa25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwgZHBpPTMwMCkKCmBgYAoKCiMgTG9hZGluZyBQYWNrYWdlcwoKYGBge3Igc2V0dXB9CiMgQ2xlYXIgZW52aXJvbm1lbnQKcm0obGlzdCA9IGxzKGFsbCA9IFRSVUUpKQoKIyBDbGVhciBlbnZpcm9ubWV0IG9mIHBhY2thZ2VzCmlmKGlzLm51bGwoc2Vzc2lvbkluZm8oKSRvdGhlclBrZ3MpID09IEZBTFNFKQogIGxhcHBseShwYXN0ZSgicGFja2FnZToiLCBuYW1lcyhzZXNzaW9uSW5mbygpJG90aGVyUGtncyksIHNlcD0iIiksCiAgICAgICAgIGRldGFjaCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFLCB1bmxvYWQgPSBUUlVFKQoKIyBMb2FkIFBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKSAKbGlicmFyeShzY2FsZXMpICNmb3JtYXQgbnVtYmVycyBhcyBjdXJyZW5jeQpsaWJyYXJ5KGhlcmUpICMgZWFzaWVyIHdheSB0byBmaW5kIGZpbGUgcGF0aApsaWJyYXJ5KGthYmxlRXh0cmEpICNpbXByb3ZlZCB0YWJsZXMKbGlicmFyeShncmlkRXh0cmEpICMgdXNlIHRvIHB1dCBncmFwaHMgdG9nZXRoZXIgaW4gdGhlIHNhbWUgZnJhbWUKbGlicmFyeShnZ3RoZW1lcykgI3RoZW1lcyBmb3IgZ3JhcGhzCmxpYnJhcnkoR0dhbGx5KSAjRURBIEFuYWx5emlzCmxpYnJhcnkocXdyYXBzMikgI05pY2VyIFN1bW1hcnkKbGlicmFyeShtYWdyaXR0cikgI2FibGVzICU8PiUKbGlicmFyeShqYW5pdG9yKSAjVG9vbHMgZm9yIEV4YW1pbmluZyBhbmQgQ2xlYW5pbmcgRGlydHkgRGF0YQpsaWJyYXJ5KHJjb21wYW5pb24pICMgdG8gcnVuIHBhaXJ3aXNlTWVkaWFuVGVzdCBmdW5jdGlvbiBpbiB0aGUgcmNvbXBhbmlvbiBwYWNrYWdlLCB3aGljaCBjb25kdWN0cyBNb29k4oCZcyBtZWRpYW4gdGVzdCBvbiBhbGwgcGFpcnMgb2YgZ3JvdXBzIGZyb20gb25lLXdheSBkYXRhCmxpYnJhcnkodG9vbHMpICMgQXBwbHkgdG9UaXRsZUNhc2UgZnVuY3Rpb24KIyBkZWZpbmUgdGhlIG1hcmt1cCBsYW5ndWFnZSB3ZSBhcmUgd29ya2luZyBpbi4KbGlicmFyeShjb2xvcnNwYWNlKQpsaWJyYXJ5KHBsb3RseSkKb3B0aW9ucyhxd3JhcHMyX21hcmt1cCA9ICJtYXJrZG93biIpCgpgYGAKCiMgSW1wb3J0aW5nIGRhdGFiYXNlCmBgYHtyIGltcG9ydGluZ19kYn0KCmdtX2RhdGEgPC0gcmVhZF9jc3YoJy9Vc2Vycy9zYWtzaGkvRG9jdW1lbnRzL1N5bGxhYnVzL0RhdGEgVmlzdWFsaXphdGlvbi9NaWR0ZXJtIFByb2plY3QvbXRwX2RhdGEuY3N2JykKI2dtX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgnTWlkdGVybSBQcm9qZWN0JywnbXRwX2RhdGEuY3N2JykpCgpnbV9kYXRhICU+JSBoZWFkKCkgJT4lIGthYmxlKCkgJT4lIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLCBmdWxsX3dpZHRoID0gVCkKYGBgCgorIF9fRmlyc3Qgb2JzZXJ2YXRpb25zOl9fCiAgLSBUaGUgZGF0YSBpcyBpbiBhIHRpZHkgZm9ybWF0ICAKICAtIFRoZXJlIGFyZSBgciBmb3JtYXQobnJvdyhnbV9kYXRhKSwgYmlnLm1hcms9IiwiLCBzY2llbnRpZmljPUZBTFNFKWAgb2JzZXJ2YXRpb25zIGFuZCBgciBuY29sKGdtX2RhdGEpYCB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgc2V0CiAgCisgX19WYXJpYWJsZXMgZXhwbGFuYXRpb25fXwogIC0gaXJpX2tleTogc3RvcmUgbnVtYmVyIChOZWVkIHRvIGJlIGEgRmFjdG9yKQogIC0gVVBDOiB1bmlxdWUgcHJvZHVjdCBudW1iZXIgKE5lZWQgdG8gYmUgYSBGYWN0b3IpCiAgLSB3ZWVrOiB3ZWVrIG9mIHNhbGUKICAtIHVuaXRzOiBudW1iZXIgb2YgY2VyZWFsIHBhY2thZ2VzIHNvbGQKICAtIGJyYW5kOiBwcm9kdWNlciBhbmQgYnJhbmQKICAtIHByb21vOiBpbiBzdG9yZSBwcm9tb3Rpb24gMC8xIGlzIG5vL3llcyAoTmVlZCB0byBiZSBhIEZhY3RvcikKICAtIHByaWNlOiBwcmljZSBwZXIgcGFja2FnZQogIC0gZmxhdm9yOiBjZXJlYWwgZmxhdm9yIGdyb3VwIChOZWVkIHRvIGJlIGEgRmFjdG9yKQogIC0gdm9sdW1lOiBjZXJlYWwgcGFja2FnZSBzaXplCiAgLSBwYWNrYWdlOiB0eXBlIG9mIGNlcmVhbCBjb250YWluZXIgKE5lZWQgdG8gYmUgYSBGYWN0b3IpCiAgLSBhZDogKE5lZWQgdG8gYmUgYSBGYWN0b3IpICAKICAgICAgTk9ORSAgCiAgICAgIEEg4oCTIG1lZGl1bSAgCiAgICAgIEIg4oCTIHNtYWxsICAKCiMgQWRqdXN0aW5nIGRhdGEKYGBge3IgYWRqdXN0aW5nLCB3YXJuaW5nPUZBTFNFfQoKZ21fZGF0YSAlPD4lIG11dGF0ZShpcmlfa2V5ID0gYXMuZmFjdG9yKGlyaV9rZXkpLAogICAgICAgICAgICAgICAgICAgIFVQQyA9IGFzLmZhY3RvcihVUEMpLAogICAgICAgICAgICAgICAgICAgIHByb21vID0gYXMuZmFjdG9yKHByb21vKSwKICAgICAgICAgICAgICAgICAgICBmbGF2b3IgPSBhcy5mYWN0b3IoZmxhdm9yKSwKICAgICAgICAgICAgICAgICAgICBhZCA9IGFzLmZhY3RvcihhZCksCiAgICAgICAgICAgICAgICAgICAgcGFja2FnZSA9IGFzLmZhY3RvcihwYWNrYWdlKQogICAgICAgICAgICAgICAgICAgICkKCiMgU2VwYXJhdGluZyBpbiBhIGJhZCB3YXkgdGhlIGJyYW5kIGFuZCBwcm9kdWNlcgpnbV9kYXRhICU8PiUgc2VwYXJhdGUoYnJhbmQsIkdFTkVSQUwgTUlMTFMgIixpbnRvPWMoImF1eCIsICJmbGF2b3JfR00iKSxyZW1vdmUgPSBGQUxTRSkgJT4lIAogICAgICAgICAgICAgIHNlcGFyYXRlKGJyYW5kLCJLRUxMT0dHUyAiLGludG89YygiYXV4IiwgImZsYXZvcl9LTCIpLHJlbW92ZSA9IEZBTFNFKSAlPiUgCiAgICAgICAgICAgICAgc2VwYXJhdGUoYnJhbmQsIlBPU1QgIixpbnRvPWMoImF1eCIsICJmbGF2b3JfUFQiKSxyZW1vdmUgPSBGQUxTRSkgJT4lIAogICAgICAgICAgICAgIG11dGF0ZShwcm9kdWNlciA9IGNhc2Vfd2hlbiggbm90KGlzLm5hKGZsYXZvcl9HTSkpIH4gIkdlbmVyYWwgTWlsbHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm90KGlzLm5hKGZsYXZvcl9LTCkpIH4gIktlbGxvZ2dzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vdChpcy5uYShmbGF2b3JfUFQpKSB+ICJQT1NUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiRVJST1IiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICBicmFuZCA9IGNhc2Vfd2hlbihwcm9kdWNlcj09IkdlbmVyYWwgTWlsbHMiIH4gdG9UaXRsZUNhc2UodG9sb3dlcihmbGF2b3JfR00pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9kdWNlcj09IktlbGxvZ2dzIiB+IHRvVGl0bGVDYXNlKHRvbG93ZXIoZmxhdm9yX0tMKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvZHVjZXI9PSJQT1NUIiB+IHRvVGl0bGVDYXNlKHRvbG93ZXIoZmxhdm9yX1BUKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICBwcm9kdWNlciA9IGFzLmZhY3Rvcihwcm9kdWNlciksCiAgICAgICAgICAgICAgICAgICAgIGJyYW5kID0gYXMuZmFjdG9yKGJyYW5kKSwKICAgICAgICAgICAgICAgICAgICAgYWQgPSBmYWN0b3IoYWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJBIiwiQiIsIk5PTkUiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk1lZGl1bSIsIlNtYWxsIiwiTm9uZSIpIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgcmV2ZW51ZSA9IHVuaXRzKnByaWNlKSAlPiUgCiAgICAgICAgICAgICAgc2VsZWN0KC1hdXgsLWZsYXZvcl9HTSwtZmxhdm9yX0tMLC1mbGF2b3JfUFQpCgpnbV9kYXRhICU+JSBzdHIoKSAKYGBgCgpfX1F1ZXN0aW9uOiBEb2VzIEZsYXZvciBnaXZlIHVzIGFueSBuZXcgaW5mb3JtYXRpb24/X18KCmBgYHtyfQpnbV9kYXRhICU+JSBncm91cF9ieShicmFuZCxmbGF2b3IpICU+JSBzdW1tYXJpc2UobigpKSAlPiUga2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksIGZ1bGxfd2lkdGggPSBUKQpgYGAKICArIEFzIHdlIGNhbiBzZWUgYWJvdmUsIHRoZSBvbmx5IGNhc2VzIHdoZW4gZmxhdm9yIGFkZHMgYSBuZXcgYnJlYWtkb3duIGFyZToKICAgIC0gQ2hlZXJpb3MgOiBUb2FzdGVkIGFuZCBSZWd1bGFyCiAgICAtIEx1Y2t5IENoYXJtczogVG9hc3RlZCBhbmQgUmVndWxhcgogICsgSG93ZXZlciwgdGhlIG51bWJlciBvZiBlbGVtZW50cyBpbiBDaGVlcmlvcyBSZWd1bGFyICg0KSBhbmQgTHVja3kgQ2hhcm1zIFJlZ3VsYXIgKDMpIGFyZSB2ZXJ5IHNtYWxsLCBzbyB3ZSBjYW5ub3QgaGF2ZSBhbnkgc3RhdGlzdGljYWwgaW5mZXJlbmNlIG9mIGRpZmZlcmVuY2UgYW1vbmcgQ2hlZXJpb3MgUmVndWxhciBhbmQgVG9hc3RlZCBvciBhbW9uZyBMdWNreSBDaGFybXMgUmVndWxhciBhbmQgVG9hc3RlZC4KICAKICAKIyBCYXNlIEVEQSAKCiMjIFN0ZXAgMTogVW5pLXZhcmlhdGUgbm9uLWdyYXBoaWNhbCBFREEgCmBgYHtyfQpnbV9kYXRhICU+JSBzdW1tYXJ5KCkKCmBgYAoKKyBfX0ZpbmRpbmdzX18KICAtIFVuaXRzIHNlZW0gdG8gYmUgc2tld2VkIHRvIHRoZSByaWdodCwgc28gd2Ugc2hvdWxkIHVzZSBtZWRpYW4gaW5zdGVhZCBvZiBtZWFuOwogIC0gQ2VyZWFscyBhcmUgYm91Z2h0IGluIEN1cHMgb25seSBgciByb3VuZChzdW0oZ21fZGF0YSRwYWNrYWdlPT0iQ1VQIikvbnJvdyhnbV9kYXRhKSoxMDAsMSlgJSBvZiB0aGUgdGltZSBhbmQgaW4gQm94ZXMgb24gdGhlIHJlc3Qgb2YgdGhlIHRpbWU7CiAgLSBWb2x1bWUgc2VlbXMgbm90IHNrZXdlZCwgdGh1cyB3ZSBjYW4gdXNlIG1lYW4gb3IgbWVkaWFuOwogIC0gUHJpY2Ugc2VlbXMgbm90IHNrZXdlZCwgdGh1cyB3ZSBjYW4gdXNlIG1lYW4gb3IgbWVkaWFuOwogIC0gQ2VyZWFscyBhcmUgaW4gcHJvbW8gYHIgcm91bmQoc3VtKGdtX2RhdGEkcHJvbW89PTEpL25yb3coZ21fZGF0YSkqMTAwLDEpYCUgb2YgdGhlIHRpbWUKICAtIENlcmVhbHMgaGF2ZSBzbWFsbCBhZHMgYHIgcm91bmQoc3VtKGdtX2RhdGEkYWQ9PSJTbWFsbCIpL25yb3coZ21fZGF0YSkqMTAwLDEpYCUgb2YgdGhlIHRpbWUgYW5kIG1lZGl1bSBhZHMgYHIgcm91bmQoc3VtKGdtX2RhdGEkYWQ9PSJNZWRpdW0iKS9ucm93KGdtX2RhdGEpKjEwMCwxKWAlIG9mIHRoZSB0aW1lOwogIC0gR2VuZXJhbCBNaWxscyByZXByZXNlbnRzIGByIHJvdW5kKHN1bShnbV9kYXRhJHByb2R1Y2VyPT0iR2VuZXJhbCBNaWxscyIpL25yb3coZ21fZGF0YSkqMTAwLDEpYCUgb2YgdGhlIHNhbGVzIHdoaWxlIEtlbGxvZ2dzIHJlcHJlc2VudHMgYHIgcm91bmQoc3VtKGdtX2RhdGEkcHJvZHVjZXI9PSJLZWxsb2dncyIpL25yb3coZ21fZGF0YSkqMTAwLDEpYCUgYW5kIFBvc3QgcmVwcmVzZW50cyBgciByb3VuZChzdW0oZ21fZGF0YSRwcm9kdWNlcj09IlBPU1QiKS9ucm93KGdtX2RhdGEpKjEwMCwxKWAlOwogCiMjIFN0ZXAgMjogVW5pdmFyaWF0ZSBncmFwaGljYWwgYW5hbHlzaXMgCgogLSBoaXN0b2dyYW1zIGFuZCBib3hwbG90cyBvZiBxdWFudGl0YXRpdmUgdmFyaWFibGVzCiAtIHVuaS12YXJpYXRlIGJhciBncmFwaHMgb2YgZmFjdG9yIHZhcmlhYmxlcwogCiMjIyB3ZWVrCmBgYHtyfQojIFVuaS12YXJpYXRlIGdyYXBoaWNhbCBhbmFseXNpcyBvZiBxdWFudGlhdGl2ZSB2YXJpYWJsZXMKZ3JpZC5hcnJhbmdlKGdtX2RhdGEgJT4lIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSB3ZWVrKSkgKyBnZW9tX2hpc3RvZ3JhbSgpLAogICAgICAgICAgICAgZ21fZGF0YSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IDEsIHkgPSB3ZWVrKSkgKyBnZW9tX2JveHBsb3QoKSArIGNvb3JkX2ZsaXAoKSwKICAgICAgICAgICAgIG5jb2wgPSAxKQpgYGAKIC0gVGhlIHdlZWtseSBzYWxlIHNlZW1zIHRvIGhhdmUgYSBwYXR0ZXJuIHRoYXQgY291bGQgYmUgZXhwbG9yZWQgaW4gdGhlIGZ1dHVyZS4KCiMjIyB1bml0cwpgYGB7cn0KIyBVbmktdmFyaWF0ZSBncmFwaGljYWwgYW5hbHlzaXMgb2YgcXVhbnRpYXRpdmUgdmFyaWFibGVzCmdyaWQuYXJyYW5nZShnbV9kYXRhICU+JSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gdW5pdHMpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSBtYXgoZ21fZGF0YSR1bml0cykpLAogICAgICAgICAgICAgZ21fZGF0YSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IDEsIHkgPSB1bml0cykpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCksCiAgICAgICAgICAgICBuY29sID0gMSkKYGBgCiAgLSBUaGUgbnVtYmVyIG9mIHVuaXRzIHNvbGQgcGVyIHRyYW5zYWN0aW9uIHZhcmllcyBhbmQgZm9sbG93cyBhIGxvZyBub3JtYWwgZGlzdHJpYnV0aW9uIGFzIGV4cGVjdGVkLiAgCiAgLSBJdCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byB1bmRlcnN0YW5kIGJldHRlciB0aGUgcmVsYXRpb25zaGlwIGFtb25nIG51bWJlciBvZiB1bml0cywgdm9sdW1lLCBwcm9tb3Rpb24sIGFuZCBhZHMgZm9yIGxhcmdlciBhbW91bnRzIG9mIHVuaXRzIGluIGEgZm9sbG93aW5nIHByb2plY3QuCiAgCiMjIyB2b2x1bWUKYGBge3J9CiMgVW5pLXZhcmlhdGUgZ3JhcGhpY2FsIGFuYWx5c2lzIG9mIHF1YW50aWF0aXZlIHZhcmlhYmxlcwpncmlkLmFycmFuZ2UoZ21fZGF0YSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHZvbHVtZSkpICsgZ2VvbV9oaXN0b2dyYW0oKSwKICAgICAgICAgICAgIGdtX2RhdGEgJT4lIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSAxLCB5ID0gdm9sdW1lKSkgKyBnZW9tX2JveHBsb3QoKSArIGNvb3JkX2ZsaXAoKSwKICAgICAgICAgICAgIG5jb2wgPSAxKQpgYGAKICAtIENlcmVhbHMgYXJlIHNvbGQgaW4gNyBtYWluIHNpemVzLgoKIyMjIFByaWNlCmBgYHtyfQojIFVuaS12YXJpYXRlIGdyYXBoaWNhbCBhbmFseXNpcyBvZiBxdWFudGlhdGl2ZSB2YXJpYWJsZXMKZ3JpZC5hcnJhbmdlKGdtX2RhdGEgJT4lIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBwcmljZSkpICsgZ2VvbV9oaXN0b2dyYW0oKSwKICAgICAgICAgICAgIGdtX2RhdGEgJT4lIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSAxLCB5ID0gcHJpY2UpKSArIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpLAogICAgICAgICAgICAgbmNvbCA9IDEpCmBgYAogIC0gUHJpY2Ugc2VlbXMgdG8gYmUgc2tld2VkLCB0aHVzIHdlIHNob3VsZCB1c2UgbWVkaWFuIGluc3RlYWQgb2YgbWVhbi4KICAKIyMjIFJldmVudWUKYGBge3J9CiMgVW5pLXZhcmlhdGUgZ3JhcGhpY2FsIGFuYWx5c2lzIG9mIHF1YW50aWF0aXZlIHZhcmlhYmxlcwpncmlkLmFycmFuZ2UoZ21fZGF0YSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJldmVudWUpKSArIGdlb21faGlzdG9ncmFtKCksCiAgICAgICAgICAgICBnbV9kYXRhICU+JSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gMSwgeSA9IHJldmVudWUpKSArIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpLAogICAgICAgICAgICAgbmNvbCA9IDEpCmBgYAogLSBSZXZlbnVlIHNlZW1zIHRvIGJlIHNrZXdlZCwgdGh1cyB3ZSBzaG91bGQgdXNlIG1lZGlhbiBpbnN0ZWFkIG9mIG1lYW4uCiAKIyMjIGlyaV9rZXkoc3RvcmVzKQpgYGB7cn0KIyBVbmktdmFyaWF0ZSBncmFwaGljYWwgYW5hbHlzaXMgb2YgZmFjdG9yIHZhcmlhYmxlcwpnbV9kYXRhICU+JSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gaXJpX2tleSkpICsgZ2VvbV9iYXIoKQpgYGAKICAKCiMjIyBmbGF2b3IKYGBge3J9CiMgVW5pLXZhcmlhdGUgZ3JhcGhpY2FsIGFuYWx5c2lzIG9mIGZhY3RvciB2YXJpYWJsZXMKZ21fZGF0YSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGZsYXZvcikpICsgZ2VvbV9iYXIoKQpgYGAKICAtIFJlZ3VsYXIgYW5kIFRvYXN0ZWQgYXJlIHRoZSBtYWluIGZsYXZvcnMuCgojIyMgcGFja2FnZQpgYGB7cn0KIyBVbmktdmFyaWF0ZSBncmFwaGljYWwgYW5hbHlzaXMgb2YgZmFjdG9yIHZhcmlhYmxlcwpnbV9kYXRhICU+JSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gcGFja2FnZSkpICsgZ2VvbV9iYXIoKQpgYGAKICAtIENvbnN1bWVycyBidXkgYWxtb3N0IG9ubHkgYm94ZXMuCgojIyMgcHJvbW8KYGBge3J9CiMgVW5pLXZhcmlhdGUgZ3JhcGhpY2FsIGFuYWx5c2lzIG9mIGZhY3RvciB2YXJpYWJsZXMKZ21fZGF0YSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHByb21vKSkgKyBnZW9tX2JhcigpCmBgYAogIC0gUHJvbW8gcGVyaW9kcyBzZWVtcyB0byBiZSBhIGdvb2Qgc2l6ZSBvZiB0aGUgZGF0YS4KICAKIyMjIGFkCmBgYHtyfQojIFVuaS12YXJpYXRlIGdyYXBoaWNhbCBhbmFseXNpcyBvZiBmYWN0b3IgdmFyaWFibGVzCmdtX2RhdGEgJT4lIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBhZCkpICsgZ2VvbV9iYXIoKQpgYGAKICAtIFByb2R1Y2VycyBzZWVtcyB0byB1c2UgbW9yZSBwcm9tbyB0aGFuIGFkcy4KCiMjIyBwcm9kdWNlcgpgYGB7cn0KIyBVbmktdmFyaWF0ZSBncmFwaGljYWwgYW5hbHlzaXMgb2YgZmFjdG9yIHZhcmlhYmxlcwpnbV9kYXRhICU+JSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gcHJvZHVjZXIpKSArIGdlb21fYmFyKCkKYGBgCiAgLSBLZWxsb2dncyBpcyB0aGUgbWFpbiBicmFuZC4KICAKIyMjIG5hbWUKYGBge3J9CiMgVW5pLXZhcmlhdGUgZ3JhcGhpY2FsIGFuYWx5c2lzIG9mIGZhY3RvciB2YXJpYWJsZXMKZ21fZGF0YSAlPiUgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGJyYW5kKSkgKyBnZW9tX2JhcigpCmBgYAoKCiMjIyBGaW5kaW5ncwogIC0gU2FsZXMgdmFyaWVzIGJldHdlZW4gdGhlIHdlZWtzLCBidXQgaXQgYXBwZWFycyB0byBmb2xsb3cgYSBwYXR0ZXJuIHdpdGggc29tZSBleHByZXNzaXZlIGRlY2xpbmVzICh3aHk/KSAgCiAgLSBDb25zdW1lciB0ZW5kIHRvIGJ1eSBzbWFsbCB1bml0cyBvZiBwcm9kdWN0cyBhbmQgdGhlIGRpc3RyaWJ1dGlvbiBzZWVtcyBsb2dhcml0aG1pYyAod2h5IHRoZXJlIGFyZSBzb21lIGdhcHMgaW4gdW5pdHM/IHdoYXQgYXJlIHRoZSB2YWx1ZXM/KQogIC0gVGhlIHZvbHVtZSBuZWFyIDEgaXMgbW9yZSB1c3VhbGx5IGJvdWdodCB0aGFuIHRoZSBvdGhlcnMuICh3aGljaCBzaXplPyBkb2VzIHByaWNlL21hcmtldCBzaGFyZSB2YXJpZXMgd2l0aCB2b2x1bWU/KQogIC0gUmVndWxhciBhbmQgVG9hc3RlZCBhcmUgdGhlIGZhdm9yaXRlIGZsYXZvcnMgYHIgcm91bmQoc3VtKG9yKGdtX2RhdGEkZmxhdm9yPT0iUkVHVUxBUiIsZ21fZGF0YSRmbGF2b3I9PSJUT0FTVEVEIikpL25yb3coZ21fZGF0YSkqMTAwLDEpYCUgb2Ygc2FsZXMKICAKICAKIyMjIFF1ZXN0aW9ucwogIC0gRG9lcyBzYWxlcyAvIHByaWNlIHZhcmllcyB3aXRoIEFkPwogIC0gRG9lcyBzYWxlcyAvIHByaWNlIHZhcmllcyB3aXRoIHByb21vPwogIC0gRG9lcyBwcm9tby9hZCBpbXBhY3RzIG1hcmtldCBzaGFyZT8KICAtIERvZXMgcHJvbW8vYWQgaW1wYWN0cyBtYXJrZXQgdm9sdW1lIC8gdW5pdHM/CiAgLSBEb2VzIHByb21vL2FkIGFuZCB3ZWVrIHZvbHVtZSBoYXMgY29ycmVsYXRpb24/IGlzIGFueXdheSBsaW5rZWQgd2l0aCB0aGUgZHJvcCBpbiBzYWxlcyBvdmVyIHNvbWUgd2Vla3M/CiAgLSBXaGF0IGlzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2b2x1bWUgYW5kIHByaWNlPwogIC0gRG9lcyB0aGUgYnJhbmRzIHVzZSB0aGUgc2FtZSBwcmljZSBzdHJhdGVneT8KICAtIFdoYXQgYXJlIHRoZSBtb3N0IGV4cGVuc2l2ZSAvIGNoZWFwZXN0IGNlcmVhbHM/IElzIHRoYXQgY29uc3RhbnQgb3ZlciB3ZWVrcz8KICAKICAKIyMgU3RlcCAzOiBNdWx0aS12YXJpYXRlIG5vbi1ncmFwaGljYWwgYW5hbHlzaXMKCiMjIyBQcm9tb3Rpb24geCBQcm9kdWNlcgoKYGBge3IgdGFibGVfUHJvbW90aW9uX3ZzX3Byb2R1Y2VyfQojIFByb3BvcnRpb24gY29udGluZ2VuY3kvY3Jvc3MgdGFibGUKZ21fZGF0YSAlPiUgCiAgdGFieWwocHJvZHVjZXIsIHByb21vKSAlPiUgCiAgYWRvcm5fdG90YWxzKHdoZXJlID0gYygicm93IiwgImNvbCIpKSAlPiUgCiAgYWRvcm5fcGVyY2VudGFnZXMoInJvdyIpICU+JQogIGFkb3JuX3BjdF9mb3JtYXR0aW5nKCkgJT4lCiAgYWRvcm5fbnMoKSAlPiUKICBrYWJsZSgpJT4lIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLCBmdWxsX3dpZHRoID0gRikKYGBgCgoKIyMjIEFkIHggUHJvZHVjZXIKCmBgYHtyIHRhYmxlX2FkX3ZzX3Byb2R1Y2VyfQojIFByb3BvcnRpb24gY29udGluZ2VuY3kvY3Jvc3MgdGFibGUKZ21fZGF0YSAlPiUgCiAgdGFieWwocHJvZHVjZXIsIGFkKSAlPiUgCiAgYWRvcm5fdG90YWxzKHdoZXJlID0gYygicm93IiwgImNvbCIpKSAlPiUgCiAgYWRvcm5fcGVyY2VudGFnZXMoInJvdyIpICU+JQogIGFkb3JuX3BjdF9mb3JtYXR0aW5nKCkgJT4lCiAgYWRvcm5fbnMoKSAlPiUKICBrYWJsZSgpJT4lIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLCBmdWxsX3dpZHRoID0gRikKYGBgCgojIyMgRmxhdm9yIHggUHJvZHVjZXIKCmBgYHtyIHRhYmxlX2ZsYXZvcl92c19wcm9kdWNlcn0KIyBQcm9wb3J0aW9uIGNvbnRpbmdlbmN5L2Nyb3NzIHRhYmxlCmdtX2RhdGEgJT4lIAogIHRhYnlsKHByb2R1Y2VyLCBmbGF2b3IpICU+JSAKICBhZG9ybl90b3RhbHMod2hlcmUgPSBjKCJyb3ciLCAiY29sIikpICU+JSAKICBhZG9ybl9wZXJjZW50YWdlcygicm93IikgJT4lCiAgYWRvcm5fcGN0X2Zvcm1hdHRpbmcoKSAlPiUKICBhZG9ybl9ucygpICU+JQogIGthYmxlKCklPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksIGZ1bGxfd2lkdGggPSBUKQpgYGAKCiMjIyBGbGF2b3IgeCBQcm9tbwoKYGBge3IgdGFibGVfZmxhdm9yX3ZzX3Byb21vfQojIFByb3BvcnRpb24gY29udGluZ2VuY3kvY3Jvc3MgdGFibGUKZ21fZGF0YSAlPiUgCiAgdGFieWwoZmxhdm9yLCBwcm9tbykgJT4lIAogIGFkb3JuX3RvdGFscyh3aGVyZSA9IGMoInJvdyIsICJjb2wiKSkgJT4lIAogIGFkb3JuX3BlcmNlbnRhZ2VzKCJyb3ciKSAlPiUKICBhZG9ybl9wY3RfZm9ybWF0dGluZygpICU+JQogIGFkb3JuX25zKCkgJT4lCiAga2FibGUoKSU+JSBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSwgZnVsbF93aWR0aCA9IEYpCmBgYAoKCiMjIyBGbGF2b3IgeCBhZAoKYGBge3IgdGFibGVfZmxhdm9yX3ZzX2FkfQojIFByb3BvcnRpb24gY29udGluZ2VuY3kvY3Jvc3MgdGFibGUKZ21fZGF0YSAlPiUgCiAgdGFieWwoZmxhdm9yLCBhZCkgJT4lIAogIGFkb3JuX3RvdGFscyh3aGVyZSA9IGMoInJvdyIsICJjb2wiKSkgJT4lIAogIGFkb3JuX3BlcmNlbnRhZ2VzKCJyb3ciKSAlPiUKICBhZG9ybl9wY3RfZm9ybWF0dGluZygpICU+JQogIGFkb3JuX25zKCkgJT4lCiAga2FibGUoKSU+JSBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSwgZnVsbF93aWR0aCA9IEYpCmBgYAoKCiMjIyBQcm9tbyB4IGFkCgpgYGB7ciB0YWJsZV9Qcm9tb3Rpb25fdnNfYWR9CiMgUHJvcG9ydGlvbiBjb250aW5nZW5jeS9jcm9zcyB0YWJsZQpnbV9kYXRhICU+JSAKICB0YWJ5bChwcm9tbywgYWQpICU+JSAKICBhZG9ybl90b3RhbHMod2hlcmUgPSBjKCJyb3ciLCAiY29sIikpICU+JSAKICBhZG9ybl9wZXJjZW50YWdlcygicm93IikgJT4lCiAgYWRvcm5fcGN0X2Zvcm1hdHRpbmcoKSAlPiUKICBhZG9ybl9ucygpICU+JQogIGthYmxlKCkKYGBgCgojIyMgUHJvZHVjZXIgeCBGbGF2b3IgeCBQcm9tbwoKYGBge3IgdGFibGVfUHJvbW90aW9uX3ZzX2ZsYXZvcl92c19wcm9tb30KIyBQcm9wb3J0aW9uIGNvbnRpbmdlbmN5L2Nyb3NzIHRhYmxlCmdtX2RhdGEgJT4lIAogIHRhYnlsKHByb2R1Y2VyLCBmbGF2b3IsIHByb21vKSAlPiUgCiAgYWRvcm5fdG90YWxzKHdoZXJlID0gYygicm93IiwgImNvbCIpKSAlPiUgCiAgYWRvcm5fcGVyY2VudGFnZXMoInJvdyIpICU+JQogIGFkb3JuX3BjdF9mb3JtYXR0aW5nKCkgJT4lCiAgYWRvcm5fbnMoKQpgYGAKCgojIyMgUHJvZHVjZXIgeCBGbGF2b3IgeCBhZAoKYGBge3IgdGFibGVfUHJvbW90aW9uX3ZzX2ZsYXZvcl92c19hZH0KIyBQcm9wb3J0aW9uIGNvbnRpbmdlbmN5L2Nyb3NzIHRhYmxlCmdtX2RhdGEgJT4lIAogIHRhYnlsKHByb2R1Y2VyLCBmbGF2b3IsIGFkKSAlPiUgCiAgYWRvcm5fdG90YWxzKHdoZXJlID0gYygicm93IiwgImNvbCIpKSAlPiUgCiAgYWRvcm5fcGVyY2VudGFnZXMoInJvdyIpICU+JQogIGFkb3JuX3BjdF9mb3JtYXR0aW5nKCkgJT4lCiAgYWRvcm5fbnMoKSAKYGBgCiAgCgojIyMgQ29ycmVsYXRpb24gVGFibGUKYGBge3J9CmdtX2RhdGEgJT4lIHNlbGVjdCh3ZWVrLCB1bml0cywgcHJpY2UsIHZvbHVtZSkgJT4lIGNvcigpIApgYGAKIAorIF9fRmluZGluZ3NfXwogIC0gVGhlIHVzZSBvZiBwcm9tb3Rpb24gc2VlbXMgc2ltaWxhciBhbW9uZyB0aGUgcHJvZHVjZXIKICAtIEdlbmVyYWwgTWlsbHMgdXNlcyBsZXNzIGFkdmVydGlzZW1lbnQgdGhhbiBLZWxsb2dncwogIC0gUG9zdCBvbmx5IHNlbGwgUmVndWxhciBGbGF2b3Igd2hpbGUgR2VuZXJhbCBNaWxscyBhbmQgS2VsbG9nZ3MgYXJlIG1vcmUgZGl2ZXJzaWZpZWQKICAtIFRoZXJlIGlzIGEgaGVhdmllciB1c2Ugb2YgYWR2ZXJ0aXNlbWVudCB3aGVuIHRoZSBwcm9kdWN0cyBhcmUgaW4gcHJvbW90aW9uLgogIC0gVGhlIHNhbGVzIHByb3BvcnRpb25zIG9mIEdlbmVyYWwgTWlsbHMgZG9lcyBub3QgZGVwZW5kIG9uIEFkcy4KICAtIENvY29hIGFuZCBGcnVpdCBmbGF2b3JzIHVzZSBtb3JlIHByb21vdGlvbiB0aGFuIFJlZ3VsYXIgYW5kIFRvYXN0ZWQgZmxhdm9ycy4gCiAgLSBDaW5uYW1vbiBUb2FzdCBpcyB0aGUgZmxhdm9yIHdpdGggbGVzcyBwcm9tbyBhbmQgbGVzcyBhZHMuCiAgLSBUaGVyZSBpcyBhIHdlZWsgY29ycmVsYXRpb24gKGByIGdtX2RhdGEgJT4lIHNlbGVjdChwcmljZSx2b2x1bWUpICU+JSBjb3IoKSAlPiUgLlsxXVsyXWApICBiZXR3ZWVuIHZvbHVtZSBhbmQgcHJpY2UuIFdlIG5lZWQgdG8gdmVyaWZ5IGlmIGl0IGlzIHN0YXRpc3RpY2FsIHNpZ25pZmljYW50LgogIAogIAogIAogIC0gRHVyaW5nIHByb21vdGlvbnMgR2VuZXJhbCBNaWxscyBzZWxsIGxlc3MgQ2lubmFtb24gVG9hc3RlZCwgUmVndWxhciwgYW5kIFRvYXN0ZWQgRmxhdm9ycwogIC0gRHVyaW5nIHByb21vdGlvbnMgS2VsbG9nZ3MgYWxzbyBzZWxsIGxlc3MgUmVndWxhciwgYW5kIFRvYXN0ZWQgRmxhdm9ycwogIC0gRHVyaW5nIHByb21vdGlvbnMgR2VuZXJhbCBNaWxscyBhbmQgS2VsbG9nZ3Mgc2VsbCBtb3JlIENvY29hIGFuZCBGcnVpdCBmbGF2b3JzCiAgCiAgCiMjIFN0ZXAgNDogTXVsdGktdmFyaWF0ZSBncmFwaGljYWwgYW5hbHlzaXMKCiMjIyBQcmljZSB2cyBQcm9kdWNlcgoKYGBge3J9CmdtX2RhdGEgJT4lIGdncGxvdChhZXMoeD1wcm9kdWNlcix5PXByaWNlKSkrZ2VvbV9ib3hwbG90KCkKZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXByaWNlLGZpbGw9cHJvZHVjZXIpKStnZW9tX2RlbnNpdHkoYWxwaGE9MC41KQpgYGAKCiAtIEFsbCB0aGUgcHJvZHVjZXJzIGhhdmUgc2ltaWxhciBtZWRpYW4gcHJpY2UKIC0gUE9TVCBoYXMgbmFycm93ZXIgaW50ZXJxdWFydGlsZSBwcmljZSBhcyBjb21wYXJlZCB0byBvdGhlciB0d28gIAoKIyMjIFByaWNlIHZzIEZsYXZvcgoKYGBge3J9CmdtX2RhdGEgJT4lIGdncGxvdChhZXMoeD1mbGF2b3IseT1wcmljZSkpK2dlb21fYm94cGxvdCgpCgptZWRfcHJpY2UgPSBnbV9kYXRhICU+JSBncm91cF9ieShmbGF2b3IpICU+JSBzdW1tYXJpc2UobWVkX3ByaWNlID0gbWVkaWFuKHByaWNlKSkKCmBgYAoKIC0gQ2lubmFtb24gVG9hc3QgYW5kIFRvYXN0ZWQgaGF2ZSBhIHNsaWdodGx5IGhpZ2hlciBtZWRpYW4gcHJpY2UgKGByIG1lZF9wcmljZSRtZWRfcHJpY2VbMV0gYCkgYXMgY29tcGFyZWQgdG8gb3RoZXJzIGFuZCBhbHNvIGEgbGFyZ2VyIGludGVycXVhcnRpbGUgcmFuZ2UKIC0gQ29jb2EgaGFzIGxlYXN0IG1lZGlhbiBwcmljZSAoYHIgbWVkX3ByaWNlJG1lZF9wcmljZVsyXWApIGFzIGNvbXBhcmVkIHRvIG90aGVyIGZsYXZvcnMKIAojIyMgUHJpY2UgdnMgRmxhdm9yIHZzIFByb2R1Y2VyCgpgYGB7cn0KZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PWZsYXZvcix5PXByaWNlLCBjb2xvcj1wcm9kdWNlcikpK2dlb21fYm94cGxvdCgpCgpgYGAKCiAtIEZvciBjb2NvYSBmbGF2b3IsIEtlbGxvZ2dzIGhhcyBhIGhpZ2hlciBtZWRpYW4gcHJpY2UgdGhhbiBHZW5lcmFsIE1pbGxzCiAtIEZvciByZWd1bGFyIGZsYXZvciwgS2VsbG9nZ3MgYWdhaW4gaGFzIGEgaGlnaGVyIG1lZGlhbiBwcmljZSB0aGFuIEdlbmVyYWwgTWlsbHMgYW5kIFBvc3QKIC0gRm9yIHRvYXN0ZWQgZmxhdm9yLCBtZWRpYW4gcHJpY2UgaXMgc2ltaWxhciBmb3IgYm90aCBLZWxsb2dncyBhbmQgR2VuZXJhbCBNaWxscwogCiMjIyBQcmljZSB2cyB2b2x1bWUKCmBgYHtyfQpnbV9kYXRhICU+JSBnZ3Bsb3QoYWVzKHg9dm9sdW1lLHk9cHJpY2UsIGNvbG9yPXByb2R1Y2VyKSkrZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyhjb2xvciA9IHByb2R1Y2VyKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkKCmdtX2RhdGEgJT4lIGdncGxvdChhZXMoeD12b2x1bWUseT1wcmljZS92b2x1bWUsIGNvbG9yPXByb2R1Y2VyKSkrZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyhjb2xvciA9IHByb2R1Y2VyKSwgbWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSkKYGBgCgogLSBQcmljZSBwZXIgdm9sdW1lIGRlY3JlYXNlcyBhcyB3aXRoIHRoZSBpbmNyZWFzZSBpbiB2b2x1bWUgYWNyb3NzIGFsbCB0aHJlZSBwcm9kdWNlcnMKIAojIyMgUmVsYXRpb25zaGlwIGFtb25nIHRoZSB2YXJpYWJsZXMKYGBge3J9CiNnbV9kYXRhICU+JSBzZWxlY3QoLXdlZWssLWlyaV9rZXksIC1VUEMpICU+JSAgZ2dwYWlycygpCgpnbV9kYXRhICU+JSBzZWxlY3QodW5pdHMsZmxhdm9yLHByb21vLGFkLHByb2R1Y2VyLHByaWNlKSAlPiUgIGdncGFpcnMoKQpgYGAKCiAtIEFmdGVyIGxvb2tpbmcgYXQgaXQsIHdlIG5vdGljZWQgdGhhdCB1bml0cyBhbmQgcHJpY2UgdmFyeSBhY3Jvc3MgcHJvbW9zIGFuZCBhZHMgaW4gZGlmZmVyZW50IGRpcmVjdGlvbnMsIHdlIGNyZWF0ZWQgYSBuZXcgdmFyaWFibGUgcmV2ZW51ZSA9IHVuaXRzICogcHJpY2UgdG8gYWNjb3VudCBmb3IgYm90aCBpbXBhY3RzIGF0IHRoZSBzYW1lIHRpbWUgCiAKIyMjIFJldmVudWUgdnMgUHJvZHVjZXIKYGBge3J9CgpnbV9kYXRhICU+JSBnZ3Bsb3QoYWVzKHg9cHJvZHVjZXIseSA9IHJldmVudWUpKSArIGdlb21fYm94cGxvdCgpCgpgYGAKCiAtIEdlbmVyYWwgTWlsbHMgYXBwZWFyIHRvIGhhdmUgYSBoaWdoZXIgcmV2ZW51ZSBmb2xsb3dlZCBieSBLZWxsb2dncyBhbmQgUG9zdAogCiMjIyBSZXZlbnVlIHZzIFByb2R1Y2VyIHZzIFByb21vCmBgYHtyfQoKZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXByb2R1Y2VyLHkgPSByZXZlbnVlLCBjb2xvcj1wcm9tbykpICsgZ2VvbV9ib3hwbG90KCkKZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXByb2R1Y2VyLHkgPSB1bml0cywgY29sb3I9cHJvbW8pKSArIGdlb21fYm94cGxvdCgpCmdtX2RhdGEgJT4lIGdncGxvdChhZXMoeD1wcm9kdWNlcix5ID0gcHJpY2UsIGNvbG9yPXByb21vKSkgKyBnZW9tX2JveHBsb3QoKQoKYGBgCgogLSBBY3Jvc3MgYWxsIHRocmVlIHByb2R1Y2VycywgbWVkaWFuIHJldmVudWUgaW5jcmVhc2VzIHdpdGggcHJvbW8KIC0gV2l0aCBwcm9tbywgS2VsbG9nZ3MgaGFzIGEgaGlnaGVyIHJhdGUgb2YgbWVkaWFuIHJldmVudWUgaW5jcmVhc2UgYXMgY29tcGFyZWQgdG8gb3RoZXIgdHdvCiAtIFdpdGggcHJvbW8sIEtlbGxvZ2dzIGFsc28gaGFzIGFuIGluY3JlYXNlIG9uIHRoZSB1cHBlciBwb3J0aW9uIG9mIGl0cyByZXZlbnVlIGludGVycXVhcnRpbGUgcmFuZ2UKIC0gQXMgbWVudGlvbmVkIGJlZm9yZSwgdGhlIHZhcmlhdGlvbiBpbiBudW1iZXIgb2YgdW5pdHMgYW5kIHByaWNlIGlzIHZlcnkgc3Ryb25nIHdoZW4gcHJvZHVjZXJzIHVzZSBwcm9tbyBidXQgaW4gb3Bwb3NpdGUgZGlyZWN0aW9uLCBzbyBpdCBiZWNvbWVzIHZlcnkgZGlmZmljdWx0IHRvIGNvbmNsdWRlIHRoZSBlZmZpY2llbmN5IHdpdGhvdXQgdXNpbmcgdGhlIHJldmVudWUgdmFyaWFibGUKIAojIyMgUmV2ZW51ZSB2cyBQcm9kdWNlciB2cyBhZHMKYGBge3J9CgpnbV9kYXRhICU+JSBnZ3Bsb3QoYWVzKHg9cHJvZHVjZXIseSA9IHJldmVudWUsIGNvbG9yPWFkKSkgKyBnZW9tX2JveHBsb3QoKQpnbV9kYXRhICU+JSBnZ3Bsb3QoYWVzKHg9cHJvZHVjZXIseSA9IHVuaXRzLCBjb2xvcj1hZCkpICsgZ2VvbV9ib3hwbG90KCkKZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXByb2R1Y2VyLHkgPSBwcmljZSwgY29sb3I9YWQpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKIC0gVGhlIG1lZGlhbiByZXZlbnVlIG9mIEdlbmVyYWwgTWlsbHMgaXMgc2xpZ2h0bHkgaGlnaGVyIHdpdGggbWVkaXVtIGFkcwogLSBUaGUgbWVkaWFuIHJldmVudWUgb2YgS2VsbG9nZ3MgaXMgc2xpZ2h0bHkgaGlnaGVyIHdpdGggc21hbGwgYWRzCiAtIFNtYWxsIGFkcyBhbHNvIGdlbmVyYXRlIG1vcmUgcmV2ZW51ZSBmb3IgUG9zdAogCiMjIyBNZWRpYW4gcmV2ZW51ZSB2cyBQcm9kdWNlciB2cyBhZHMKYGBge3J9Cm1lZF9hZHNfcHJvZCA8LSBnbV9kYXRhICU+JSBncm91cF9ieShwcm9kdWNlcixhZCkgJT4lIHN1bW1hcmlzZShtZWRfcmV2ZW51ZSA9IG1lZGlhbihyZXZlbnVlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBuKCkpCm1lZF9hZHNfcHJvZCAlPiUgZ2dwbG90KGFlcyh4ID0gcHJvZHVjZXIsIHkgPSBtZWRfcmV2ZW51ZSwgZmlsbCA9IGFkKSkgKyBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZG9kZ2UnKSArIGNvb3JkX2ZsaXAoKQpgYGAKCiMjIyBNZWRpYW4gcmV2ZW51ZSB2cyBQcm9kdWNlciB2cyBwcm9tbwpgYGB7cn0KbWVkX3Byb21vX3Byb2QgPC0gZ21fZGF0YSAlPiUgZ3JvdXBfYnkocHJvZHVjZXIscHJvbW8pICU+JSBzdW1tYXJpc2UobWVkX3JldmVudWUgPSBtZWRpYW4ocmV2ZW51ZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbigpKQptZWRfcHJvbW9fcHJvZCAlPiUgZ2dwbG90KGFlcyh4ID0gcHJvZHVjZXIsIHkgPSBtZWRfcmV2ZW51ZSwgZmlsbCA9IHByb21vKSkgKyBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZG9kZ2UnKSArIGNvb3JkX2ZsaXAoKQpgYGAKCiMjIyBNZWRpYW4gcmV2ZW51ZSB2cyBmbGF2b3IgdnMgYWRzCmBgYHtyfQptZWRfYWRzX2ZsYXZvciA8LSBnbV9kYXRhICU+JSBncm91cF9ieShmbGF2b3IsYWQpICU+JSBzdW1tYXJpc2UobWVkX3JldmVudWUgPSBtZWRpYW4ocmV2ZW51ZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbigpKQptZWRfYWRzX2ZsYXZvciAlPiUgZ2dwbG90KGFlcyh4ID0gZmxhdm9yLCB5ID0gbWVkX3JldmVudWUsIGZpbGwgPSBhZCkpICsgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2RvZGdlJykgKyBjb29yZF9mbGlwKCkKCmdnc2F2ZSgncmV2ZW51ZV9mbGF2b3JfYWRzLnBuZycpCgpgYGAKCiAtIFRoZSBtZWRpYW4gcmV2ZW51ZSBmb3IgYSBmbGF2b3IgcmVkdWNlcyBhcyB0aGUgbGVuZ3RoIG9mIHRoZSBhZCBpbmNyZWFzZXMgZXhjZXB0IGZvciBUb2FzdGVkIGFuZCBDaW5uYW1vbiBUb2FzdAogCiMjIyBNZWRpYW4gcmV2ZW51ZSB2cyBmbGF2b3IgdnMgcHJvbW8KYGBge3J9Cm1lZF9wcm9tb19mbGF2b3IgPC0gZ21fZGF0YSAlPiUgZ3JvdXBfYnkoZmxhdm9yLHByb21vKSAlPiUgc3VtbWFyaXNlKG1lZF9yZXZlbnVlID0gbWVkaWFuKHJldmVudWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IG4oKSkKbWVkX3Byb21vX2ZsYXZvciAlPiUgZ2dwbG90KGFlcyh4ID0gZmxhdm9yLCB5ID0gbWVkX3JldmVudWUsIGZpbGwgPSBwcm9tbykpICsgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2RvZGdlJykgKyBjb29yZF9mbGlwKCkKYGBgCgogLSBUaGUgbWVkaWFuIHJldmVudWUgZm9yIGFsbCB0aGUgZmxhdm9ycyBpbmNyZWFzZSB3aXRoIHByb21vCiAKIyMjIE1lZGlhbiByZXZlbnVlIHZzIGJyYW5kIHZzIGFkcwpgYGB7cn0KbWVkX2Fkc19icmFuZCA8LSBnbV9kYXRhICU+JSBncm91cF9ieShwcm9kdWNlciwgYnJhbmQsIGFkKSAlPiUgc3VtbWFyaXNlKG1lZF9yZXZlbnVlID0gbWVkaWFuKHJldmVudWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IG4oKSkKbWVkX2Fkc19icmFuZCAlPiUgZ2dwbG90KGFlcyh4ID0gcGFzdGUocHJvZHVjZXIsYnJhbmQpLCB5ID0gbWVkX3JldmVudWUsIGZpbGwgPSBhZCkpICsgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2RvZGdlJykgKyBjb29yZF9mbGlwKCkKYGBgCgogLSBJbnRlcmVzdGluZ2x5LCBDaGVlcmlvcyBmcm9tIEdlbmVyYWwgTWlsbHMgbG9zZSBtZWRpYW4gcmV2ZW51ZSB3aXRoIGFkcwogLSBBbHNvLCBGcm9zdGVkIG1pbmkgd2hlYXRzIGZyb20gS2VsbG9nZ3MgaXMgbG9zaW5nIG1lZGlhbiByZXZlbnVlIHdpdGggYWRzCiAtIEtlbGxvZ2dzIFJhaXNpbiBCcmFuIGhhcyBhIGhpZ2hlciBtZWRpYW4gcmV2ZW51ZSB3aXRoIG1lZGl1bSBhZHMKIAojIyMgTWVkaWFuIHJldmVudWUgdnMgYnJhbmQgdnMgcHJvbW8KYGBge3J9Cm1lZF9wcm9tb19icmFuZCA8LSBnbV9kYXRhICU+JSBncm91cF9ieShwcm9kdWNlciwgYnJhbmQscHJvbW8pICU+JSBzdW1tYXJpc2UobWVkX3JldmVudWUgPSBtZWRpYW4ocmV2ZW51ZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbigpKQptZWRfcHJvbW9fYnJhbmQgJT4lIGdncGxvdChhZXMoeCA9IHBhc3RlKHByb2R1Y2VyLGJyYW5kKSwgeSA9IG1lZF9yZXZlbnVlLCBmaWxsID0gcHJvbW8pKSArIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9ICdkb2RnZScpICsgY29vcmRfZmxpcCgpCgpnZ3NhdmUoJ3JldmVudWVfYnJhbmRfcHJvbW8ucG5nJykKCmBgYAoKIC0gSW50ZXJlc3RpbmdseSwgQ2hlZXJpb3MgZnJvbSBHZW5lcmFsIE1pbGxzIGxvc2UgbWVkaWFuIHJldmVudWUgd2l0aCBwcm9tbwogCiMjIyBNZWRpYW4gcmV2ZW51ZSB2cyBwcm9kdWNlciB2cyBwcm9tbyBhbmQgQWRzCmBgYHtyfQptZWRfYWRzX3Byb21vX3Byb2R1Y2VyIDwtIGdtX2RhdGEgJT4lIGdyb3VwX2J5KHByb2R1Y2VyLCBhZCwgcHJvbW8pICU+JSBzdW1tYXJpc2UobWVkX3JldmVudWUgPSBtZWRpYW4ocmV2ZW51ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IG4oKSkKbWVkX2Fkc19wcm9tb19wcm9kdWNlciAlPiUgZ2dwbG90KGFlcyh4ID0gcHJvZHVjZXIsIHkgPSBtZWRfcmV2ZW51ZSwgZmlsbCA9IHBhc3RlKHByb21vLGFkKSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2RvZGdlJykgKyBjb29yZF9mbGlwKCkKCmdtX2RhdGEgJT4lIGdncGxvdChhZXMoeCA9IHByb2R1Y2VyLCB5ID0gcmV2ZW51ZSxjb2xvcj1wYXN0ZShwcm9tbyxhZCkpKSArIAogIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpCgpgYGAKCiAtIEdlbmVyYWwgTWlsbHMgbWFrZSBtb3N0IG1lZGlhbiByZXZlbnVlIG9uIG1lZGl1bSBhZHMgd2l0aCBwcm9tbyBmb2xsb3dlZCBieSBzbWFsbCBhZHMgd2l0aG91dCBwcm9tbwogLSBLZWxsb2dncyBtYWtlIG1vc3QgbWVkaWFuIHJldmVudWUgb24gc21hbGwgYWRzIHdpdGggcHJvbW8gZm9sbG93ZWQgYnkgbWVkaXVtIGFkcyB3aXRoIHByb21vCgogCjwhLS0gCiMjIyBNZWRpYW4gcmV2ZW51ZSByZXZlbnVlIGJyYW5kIHZzIHByb21vIGFuZCBBZHMKYGBge3J9Cm1lZF9hZHNfcHJvbW9fYnJhbmRfcHJvZHVjZXIgPC0gZ21fZGF0YSAlPiUgZ3JvdXBfYnkocHJvZHVjZXIsIGJyYW5kLCBhZCwgcHJvbW8pICU+JSBzdW1tYXJpc2UobWVkX3JldmVudWUgPSBtZWRpYW4ocmV2ZW51ZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbigpKQprYWJsZSgKICAgIG1lZF9hZHNfcHJvbW9fYnJhbmRfcHJvZHVjZXIsCiAgICBjYXB0aW9uID0gIl9fY2FwdGlvbl9fIiwKICAgIHJvdy5uYW1lcyA9IFRSVUUsCiAgICBjb2wubmFtZXMgPSBjKCJQcm9kdWNlciIsIkJyYW5kIiwiQWR2IiwiUHJvbW90aW9uIiwiTWVkaWFuIFJldmVudWUiLCJRdWFudGl0eSIpLAogICAgYWxpZ24gPSAnYycgIAogICAgKSAlPiUgIAogICAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSwgZnVsbF93aWR0aCA9IFQpCmBgYAotLS0+CgojIyMgVm9sdW1lIGRpc3RyaWJ1dGlvbgpgYGB7cn0KZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXZvbHVtZSkpICsgZ2VvbV9kZW5zaXR5KCkrZmFjZXRfd3JhcCggfiBwcm9kdWNlcikKZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXZvbHVtZSxmaWxsPWFkKSkgKyBnZW9tX2RlbnNpdHkoYWxwaGE9MC40KStmYWNldF9ncmlkKHJvd3M9IHZhcnMocHJvZHVjZXIpKQoKYGBgCgogIC0gRm9yIFBvc3QsIGR1cmluZyBhZCBwZXJpb2RzLCB0aGVyZSBpcyBhIGNvbmNlbnRyYXRpb24gb2Ygdm9sdW1lcyBpbiA0IGRpZmZlcmVudCBzaXplcwoKIyMjIFVuaXRzIGRpc3RyaWJ1dGlvbgpgYGB7cn0KZ21fZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXVuaXRzKSkgKyBnZW9tX2RlbnNpdHkoKStmYWNldF93cmFwKCB+IHByb2R1Y2VyKQpnbV9kYXRhICU+JSBnZ3Bsb3QoYWVzKHg9dW5pdHMsZmlsbD1hZCkpICsgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNCkrZmFjZXRfZ3JpZChyb3dzPSB2YXJzKHByb2R1Y2VyKSkKCmBgYAoKICAtIFdoZW4gd2UgaGF2ZSBhZHMsIHBlb3BsZSB0ZW5kIHRvIGJ1eSBtb3JlIHVuaXRzIHRoYW4gaW4gbm8gYWRzIHBlcmlvZHMuCiAgCisgX19JbiBzdW1tYXJ5Ol9fCiAgMS4gR2VuZXJhbCBNaWxscyBzZWVtcyB0byBoYXZlIGEgaGlnaGVyIHJldmVudWUgZHVyaW5nIGEgUHJvbW8gcGVyaW9kIHdpdGggTWVkaXVtIHNpemUgYWRzOyAKICAyLiBLZWxsb2dncyBhcHBlYXJzIHRvIGRvIGJldHRlciBpbiBhIFByb21vIHdpdGggc21hbGwgc2l6ZSBhZHMgc3RyYXRlZ3k7CiAgMy4gVGhlIGRhdGEgc3VnZ2VzdHMgdGhhdCBQb3N0IGRvIGJldHRlciBkdXJpbmcgcHJvbW8gYW5kIHNtYWxsIHNpemUgc3RyYXRlZ3k7IAogIDMuIEl0IGlzIGludGVyZXN0aW5nIHRvIG5vdGljZSB0aGF0IEdlbmVyYWwgTWlsbHMgQ2hlZXJpb3MgaGFzIGEgbG93ZXIgbWVkaWFuIHJldmVudWUgZHVyaW5nIHByb21vIGFuZCBhZHMuIFRoZSBzYW1lIGFwcGVhcnMgdG8gYmUgdHJ1ZSBmb3IgS2VsbG9nZ3MgRnJvc3RlZCBNaW5pIFdoZWF0czsKICA0LiBGcnVpdHMgYW5kIENvY29hIGZsYXZvciBzaG93IHNpZ25zIHRoYXQgaGF2ZSB0aGUgaGlnaGVzdCBpbXBhY3Qgb24gcmV2ZW51ZSBhbW9uZyB0aGUgZmxhdm9ycyBkdXJpbmcgc21hbGwgYWQgY2FtcGFpZ25zOwogIAoKIyBUZXN0aW5nIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQoKIyMgR2VuZXJhbCBNaWxscyBoYXMgaGlnaGVyIHJldmVudWUgaW4gcHJvbW8gcGVyaW9kcyB3aXRoIG1lZGl1bSBzaXplIGFkcwoKICBBcyB3ZSBzYXcgZWFybGllciBpbiB0aGlzIGRvY3VtZW50IHNpbmNlIHJldmVudWUgaXMgYSBza2V3ZWQgbWVhc3VyZSB3ZSBzaG91bGQgdXNlIHRoZSBtZWRpYW4gaW5zdGVhZCBvZiB0aGUgbWVhbi4gVG8gdGVzdCBpZiB0aGUgbWVkaWFucyBhcmUgZGlmZmVyZW50IHdlIHdpbGwgdXRpbGl6ZSB0aGUgTW9vZOKAmXMgbWVkaWFuIHRlc3QuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbWVkaWFuX3Rlc3QgPC0gZ21fZGF0YSAlPiUgZmlsdGVyKHByb2R1Y2VyID09ICJHZW5lcmFsIE1pbGxzIikgJT4lIG11dGF0ZShwcm9tb19hZCA9IHBhc3RlKHByb21vLGFkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9tb19hZCA9IGFzLmZhY3Rvcihwcm9tb19hZCkpCgpQVCA9IHBhaXJ3aXNlTWVkaWFuTWF0cml4KHJldmVudWUgfiBwcm9tb19hZCwKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSAgID0gbWVkaWFuX3Rlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgIGV4YWN0ICA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJib25mZXJyb25pIikKa2FibGUoUFQkQWRqdXN0ZWQpICU+JSBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSwgZnVsbF93aWR0aCA9IFQpCmBgYAoKVGh1cywgd2UgY2FuIGNvbmNsdWRlIHRoYXQgR2VuZXJhbCBNaWxscycgbWVkaWFuIHJldmVudWUgZHVyaW5nIHByb21vIGFuZCBtZWRpdW0gYWRzIChtZWRpYW4gcmV2ZW51ZSA9YHIgZG9sbGFyKG1lZF9hZHNfcHJvbW9fcHJvZHVjZXIkbWVkX3JldmVudWVbMl0pYCApIGlzIHN0YXRpc3RpY2FsbHkgZGlmZmVyZW50IGZyb20gbm8gcHJvbW8gd2l0aCBubyBhZHMgKG1lZGlhbiByZXZlbnVlID0gYHIgZG9sbGFyKG1lZF9hZHNfcHJvbW9fcHJvZHVjZXIkbWVkX3JldmVudWVbNV0pYCwgcC12YWx1ZSBhZGp1c3RlZCA9IGByIFBUJEFkanVzdGVkWzQsMl0qMTAwYCUpIGF0IDk1JSBjb25maWRlbmNlIGxldmVsLCBhbmQgdGhhdCBHZW5lcmFsIE1pbGxzJyBtZWRpYW4gcmV2ZW51ZSwgZHVyaW5nIHByb21vIGFuZCBubyBhZHMobWVkaWFuIHJldmVudWUgPSBgciBkb2xsYXIobWVkX2Fkc19wcm9tb19wcm9kdWNlciRtZWRfcmV2ZW51ZVs2XSlgKSwgaXMgYWxzbyBkaWZmZXJlbnQgZnJvbSBubyBwcm9tbyBhbmQgbm8gYWRzIChwLXZhbHVlIGFkanVzdGVkID0gYHIgUFQkQWRqdXN0ZWRbNSwyXSoxMDBgJSkgYXQgOTUlIGNvbmZpZGVuY2UgbGV2ZWwuCgpBY2NvcmRpbmcgdG8gdGhlIGRhdGEgc2V0LCB3aGVuIEdlbmVyYWwgTWlsbHMgdXNlcyBwcm9tbyBhbmQgbWVkaXVtIGFkcywgdGhlIG1lZGlhbiByZXZlbnVlIGluY3JlYXNlcyBhcm91bmQgYHIgcm91bmQobWVkX2Fkc19wcm9tb19wcm9kdWNlciRtZWRfcmV2ZW51ZVsyXS9tZWRfYWRzX3Byb21vX3Byb2R1Y2VyJG1lZF9yZXZlbnVlWzVdLTEsMykqMTAwYCUgaW4gY29tcGFyaXNvbiB0byB0aGUgYmFzZSBjYXNlIHNjZW5hcmlvKG5vIHByb21vIGFuZCBubyBhZHMpLiBTaW1pbGFybHksIHdpdGggcHJvbW8gYW5kIG5vIGFkcyB0aGUgbWVkaWFuIHJldmVudWUgaW5jcmVhc2VzIGFyb3VuZCBgciByb3VuZChtZWRfYWRzX3Byb21vX3Byb2R1Y2VyJG1lZF9yZXZlbnVlWzZdL21lZF9hZHNfcHJvbW9fcHJvZHVjZXIkbWVkX3JldmVudWVbNV0tMSwzKSoxMDBgJSBpbiBjb21wYXJpc29uIHRvIHRoZSBiYXNlIGNhc2Ugc2NlbmFyaW8uCgpBbHRob3VnaCB0aGUgbWVkaWFuIHJldmVudWUgZnJvbSB0aGUgc2NlbmFyaW8gb2YgcHJvbW8gYW5kIG1lZGl1bSBhZHMgaXMgbm90IHN0YXRpc3RpY2FsbHkgZGlmZmVyZW50IGZyb20gdGhlIGNhc2Ugd2l0aCBwcm9tbyBhbmQgbm8gYWRzIChwLXZhbHVlIGFkanVzdGVkID0gYHIgUFQkQWRqdXN0ZWRbNCw2XSoxMDBgJSApIHRoZSBpbmNyZWFzZSBvZiByZXZlbnVlIGlzIHByYWN0aWNhbGx5IHNpZ25pZmljYW50IChpbmNyZWFzZSBvZiBgciByb3VuZChtZWRfYWRzX3Byb21vX3Byb2R1Y2VyJG1lZF9yZXZlbnVlWzJdL21lZF9hZHNfcHJvbW9fcHJvZHVjZXIkbWVkX3JldmVudWVbNl0tMSwzKSoxMDBgJSksIHRodXMgd2UgY29uY2x1ZGUgdGhhdCBkb2luZyBhIG1lZGl1bSBhZCBjYW1wYWlnbiBpcyBpbXBvcnRhbnQgYW5kIHNob3VsZCBiZSBjb25zaWRlcmVkIHRvIGJlIHVzZWQgd2l0aCBwcm9tbyBldmVuIHRob3VnaCB0aGUgaW5jcmVhc2UgaW4gcmV2ZW51ZSBpcyBub3QgcmVsaWFibGUuCgpgYGB7cn0KbWVkX2Fkc19wcm9tb19HTSA8LSBnbV9kYXRhICU+JSBmaWx0ZXIocHJvZHVjZXIgPT0gIkdlbmVyYWwgTWlsbHMiKSAlPiUgZ3JvdXBfYnkoYWQsIHByb21vKSAlPiUgc3VtbWFyaXNlKG1lZF9yZXZlbnVlID0gbWVkaWFuKHJldmVudWUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBuKCkpICU+JSB1bmdyb3VwKCkKc3ViX3RpdGxlID0gcGFzdGUwKCJJbiB0aGF0IHNjZW5hcmlvIG1lZGlhbiByZXZlbnVlIGluY3JlYXNlcyAiLAogICAgICAgICAgICAgICAgICAgcm91bmQobWVkX2Fkc19wcm9tb19wcm9kdWNlciRtZWRfcmV2ZW51ZVsyXS9tZWRfYWRzX3Byb21vX3Byb2R1Y2VyJG1lZF9yZXZlbnVlWzVdLTEsMykqMTAwLAogICAgICAgICAgICAgICAgICAgIiUgYXMgY29tcGFyZWQgXG50byB0aGUgYmFzZSBjYXNlIG9mIG5vIFByb21vIGFuZCBubyBBZHMiKQogICAgICAgICAgCm1lZF9hZHNfcHJvbW9fR00gJTw+JSBtdXRhdGUoc2NlbmFyaW8gPSBhcy5mYWN0b3IocGFzdGUocHJvbW8sYWQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHNjZW5hcmlvID0gZmFjdG9yKHNjZW5hcmlvLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCIwIE5vbmUiLCIwIFNtYWxsIiwiMCBNZWRpdW0iLCAiMSBOb25lIiwiMSBTbWFsbCIsICIxIE1lZGl1bSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJObyBQcm9tbywgTm8gQWQiLCJObyBQcm9tbywgU21hbGwgQWQiLCJObyBQcm9tbywgTWVkaXVtIEFkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXaXRoIFByb21vLCBObyBBZCIsIldpdGggUHJvbW8sIFNtYWxsIEFkIiwiV2l0aCBQcm9tbywgTWVkaXVtIEFkIikgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICApCm1lZF9hZHNfcHJvbW9fR00gJT4lIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoc2NlbmFyaW8sLWFzLm51bWVyaWMoc2NlbmFyaW8pKSwgeSA9IG1lZF9yZXZlbnVlLCBmaWxsID0gc2NlbmFyaW8pKSArIAogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9ICdkb2RnZScpICsgY29vcmRfZmxpcCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gZG9sbGFyKSArCiAgZ2d0aXRsZSgiVGhlIGhpZ2hlc3QgbWVkaWFuIHJldmVudWUgZm9yIEdlbmVyYWwgTWlsbHMKaXMgd2hlbiB1c2luZyBwcm9tbyBhbmQgbWVkaXVtIGFkcyIsCiAgICAgICAgICBzdWIgPSBzdWJfdGl0bGUpICsKICBsYWJzKHggPSAiIiwgeSA9ICJNZWRpYW4gUmV2ZW51ZSIpICsKICB0aGVtZV9lY29ub21pc3Rfd2hpdGUoZ3JheV9iZyA9RkFMU0UpICsgc2NhbGVfY29sb3VyX2Vjb25vbWlzdCgpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJncmV5NjUiLCAiZ3JleTY1IiwgImdyZXk2NSIsICJncmV5NjUiLCJncmV5NjUiLCJncmV5MjUiKSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpnZ3NhdmUoZmlsZW5hbWUgPSAiR2VuZXJhbF9NaWxscy5wbmciKQpgYGAKCgoKIyMgR2VuZXJhbCBNaWxscyBDaGVlcmlvcyBoYXMgYSBsb3dlciBtZWRpYW4gcmV2ZW51ZSBkdXJpbmcgcHJvbW8gYW5kIGFkcwoKYGBge3J9CgptZWRpYW5fdGVzdF9jaGUgPC0gZ21fZGF0YSAlPiUgZmlsdGVyKGJyYW5kID09ICJDaGVlcmlvcyIpICU+JSBtdXRhdGUocHJvbW9fYWQgPSBwYXN0ZShwcm9tbyxhZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb21vX2FkID0gYXMuZmFjdG9yKHByb21vX2FkKSkKClBUID0gcGFpcndpc2VNZWRpYW5NYXRyaXgocmV2ZW51ZSB+IHByb21vX2FkLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhICAgPSBtZWRpYW5fdGVzdF9jaGUsCiAgICAgICAgICAgICAgICAgICAgICAgIGV4YWN0ICA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJib25mZXJyb25pIikKCmthYmxlKFBUJEFkanVzdGVkKSAlPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiksIGZ1bGxfd2lkdGggPSBUKQoKZ21fZGF0YV9jaGUgPC0gZ21fZGF0YSAlPiUgZmlsdGVyKGJyYW5kID09ICJDaGVlcmlvcyIpICU+JSBtdXRhdGUocHJvbW9fYWQgPSBwYXN0ZShwcm9tbyxhZCkpICU+JSBncm91cF9ieShwcm9tb19hZCkgJT4lIHN1bW1hcmlzZShtZWRfcmV2ID0gbWVkaWFuKHJldmVudWUpKQoKCmBgYAoKRm9yIHRoZSBudWxsIGh5cG90aGVzaXMgb2YgQ2hlZXJpb3MgbWVkaWFuIHJldmVudWUgdW5kZXIgcHJvbW8gYW5kIG5vIGFkcyBiZWluZyBlcXVhbCB0byBtZWRpYW4gcmV2ZW51ZSB1bmRlciBubyBwcm9tbyBhbmQgbm8gYWRzLCB3ZSBkb24ndCBoYXZlIGEgc3Ryb25nIGVub3VnaCBldmlkZW5jZSAocC12YWx1ZSA9IGByIFBUJEFkanVzdGVkWzUsMl1gKSB0byByZWplY3QgaXQgYXQgYSBjb25maWRlbmNlIGxldmVsIG9mIDk1JS4gSG93ZXZlciwgYXQgYSBjb25maWRlbmNlIGxldmVsIG9mIDg1JSwgd2UgaGF2ZSBzdWZmaWNpZW50IGV2aWRlbmNlIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLgoKQWNjb3JkaW5nIHRvIHRoZSBkYXRhLCBDaGVlcmlvcycgbWVkaWFuIHJldmVudWUgaXMgYHIgLXJvdW5kKGdtX2RhdGFfY2hlJG1lZF9yZXZbNV0gLyBnbV9kYXRhX2NoZSRtZWRfcmV2WzJdIC0gMSwzKSoxMDBgJSBsb3dlciB3aGVuIGluIHByb21vIGFuZCBubyBhZHMgYXMgY29tcGFyZWQgdG8gd2hlbiBub3QgaW4gcHJvbW8gYW5kIG5vIGFkcywgdGh1cyB3ZSBjb25jbHVkZSB0aGF0IEdlbmVyYWwgTWlsbHMgc2hvdWxkIG5vdCB1c2UgcHJvbW9zIGZvciBDaGVlcmlvcy4KCmBgYHtyfQoKc3ViX3RpdGxlMSA9IHBhc3RlMCgiVGhlIG1lZGlhbiByZXZlbnVlIG9mIENoZWVyaW9zIGRlY2xpbmVzIGJ5ICIsLXJvdW5kKGdtX2RhdGFfY2hlJG1lZF9yZXZbNV0gLyBnbV9kYXRhX2NoZSRtZWRfcmV2WzJdIC0gMSwzKSoxMDAsCiAgICAgICAgICAgICAgICAgICAiJSB3aGVuIGluIHByb21vdGlvbiIpCgptZWRfY2hlX3Byb21vIDwtIGdtX2RhdGEgJT4lIGZpbHRlcihwcm9kdWNlciA9PSAiR2VuZXJhbCBNaWxscyIpICU+JSBncm91cF9ieShicmFuZCxwcm9tbykgJT4lIHN1bW1hcmlzZShtZWRfcmV2ZW51ZSA9IG1lZGlhbihyZXZlbnVlKSwgbiA9IG4oKSkKCm1lZF9jaGVfcHJvbW8gJT4lIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoYnJhbmQsLW1lZF9yZXZlbnVlKSwgeSA9IG1lZF9yZXZlbnVlLCBmaWxsID0gZmFjdG9yKHByb21vLCBsZXZlbHMgPSByZXYobGV2ZWxzKHByb21vKSkpKSkgKyBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgIHBvc2l0aW9uID0gImRvZGdlIikgKyAKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeCA9ICIiLCB5ID0gIk1lZGlhbiBSZXZlbnVlIiwgdGl0bGUgPSAiQ2hlZXJpb3MgaXMgdGhlIG9ubHkgR2VuZXJhbCBNaWxscyBicmFuZCB3aG9zZSBcbm1lZGlhbiByZXZlbnVlIGRlY3JlYXNlcyB3aXRoIHByb21vdGlvbiIsc3VidGl0bGUgPSBzdWJfdGl0bGUxKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBkb2xsYXIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdncmV5NzUnLCdncmV5MjUnKSwgbmFtZSA9ICIiLCBsYWJlbHMgPSBjKCJXaXRoIFByb21vIiwiV2l0aG91dCBQcm9tbyIpKSArCiAgdGhlbWVfZWNvbm9taXN0X3doaXRlKGdyYXlfYmcgPUZBTFNFKSArIHNjYWxlX2NvbG91cl9lY29ub21pc3QoKSsKICB0aGVtZShheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkKZ2dzYXZlKGZpbGVuYW1lID0gIkNoZWVyaW9zLnBuZyIpCmBgYAoKYGBge3J9CiNCZWdpbm5pbmcKY29tcGFyaXNvbl9jaGFydCA8LSBnbV9kYXRhICU+JSBncm91cF9ieShwcm9kdWNlciwgd2VlaykgJT4lIHN1bW1hcmlzZSh0b3RhbF9yZXYgPSBzdW0ocmV2ZW51ZSkpICU+JSBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gd2VlaywgeSA9IHRvdGFsX3JldiwgY29sb3IgPSBwcm9kdWNlcikpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9ICdUb3RhbCByZXZlbnVlIGdlbmVyYXRlZCBieSBLZWxsb2dncyBpcyBtdWNoIGhpZ2hlciB0aGFuIEdlbmVyYWwgTWlsbHMnLCB4ID0gJ1dlZWsnLCB5ID0gJ1RvdGFsIFJldmVudWUnLCBzdWJ0aXRsZSA9ICcnKSArCiAgdGhlbWVfZWNvbm9taXN0X3doaXRlKGdyYXlfYmcgPUZBTFNFKSArCiAgI3RoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ0ZBTFNFJykgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnZ3JleSAyNScsJ2RhcmsgcmVkJywnbGlnaHQgZ3JleScpKSArCiAgI3NjYWxlX2NvbG9yX2Rpc2NyZXRlX3F1YWxpdGF0aXZlKHBhbGV0dGUgPSAnV2FybScpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxNTA0LCB5ID0gODAwMCwgbGFiZWwgPSAiS2VsbG9nZ3MiLCBjb2xvciA9ICdkYXJrIHJlZCcpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxNTEwLCB5ID0gNTcwMCwgbGFiZWwgPSAiR2VuZXJhbCBNaWxscyIsIGNvbG9yID0gJ2dyZXkgMjUnKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTUxMCwgeSA9IDE3MDAsIGxhYmVsID0gIlBvc3QiLCBjb2xvciA9ICdkYXJrIGdyZXknKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcikKY29tcGFyaXNvbl9jaGFydApnZ3NhdmUoJ2NvbXBhcmlzb25fY2hhcnQucG5nJyxwbG90ID0gY29tcGFyaXNvbl9jaGFydCkKCiNNaWRkbGUxCm1lZF9hZHNfcHJvbW9fR00gPC0gZ21fZGF0YSAlPiUgZmlsdGVyKHByb2R1Y2VyID09ICJHZW5lcmFsIE1pbGxzIikgJT4lIGdyb3VwX2J5KGFkLCBwcm9tbykgJT4lIHN1bW1hcmlzZShtZWRfcmV2ZW51ZSA9IG1lZGlhbihyZXZlbnVlKSxuID1uKCkpICU+JSB1bmdyb3VwKCkKbWVkX2Fkc19wcm9tb19HTSAlPD4lIG11dGF0ZShzY2VuYXJpbyA9IGFzLmZhY3RvcihwYXN0ZShwcm9tbyxhZCkpLHNjZW5hcmlvID0gZmFjdG9yKHNjZW5hcmlvLGxldmVscyA9IGMoIjAgTm9uZSIsIjAgU21hbGwiLCIwIE1lZGl1bSIsICIxIE5vbmUiLCIxIFNtYWxsIiwgIjEgTWVkaXVtIiksbGFiZWxzID0gYygiTm8gUHJvbW8sIE5vIEFkIiwiTm8gUHJvbW8sIFNtYWxsIEFkIiwiTm8gUHJvbW8sIE1lZGl1bSBBZCIsIldpdGggUHJvbW8sIE5vIEFkIiwiV2l0aCBQcm9tbywgU21hbGwgQWQiLCJXaXRoIFByb21vLCBNZWRpdW0gQWQiKSkpCmdlbmVyYWxfbWlsbHNfY2hhcnQgPC0gbWVkX2Fkc19wcm9tb19HTSAlPiUgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihzY2VuYXJpbywtYXMubnVtZXJpYyhzY2VuYXJpbykpLCB5ID0gbWVkX3JldmVudWUsIGZpbGwgPSBzY2VuYXJpbykpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2RvZGdlJykgKyAKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBkb2xsYXIpICsKICBnZ3RpdGxlKCJUaGUgaGlnaGVzdCBtZWRpYW4gcmV2ZW51ZSBmb3IgR2VuZXJhbCBNaWxscyBpcyB3aGVuIHVzaW5nIHByb21vIGFuZCBtZWRpdW0gYWRzIiwgc3ViID0gJ3N1Yl90aXRsZScpICsKICBsYWJzKHggPSAiIiwgeSA9ICJNZWRpYW4gUmV2ZW51ZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogICN0aGVtZV9lY29ub21pc3Rfd2hpdGUoZ3JheV9iZyA9RkFMU0UpICsgc2NhbGVfY29sb3VyX2Vjb25vbWlzdCgpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJncmV5NjUiLCAiZ3JleTY1IiwgImdyZXk2NSIsICJncmV5NjUiLCJncmV5NjUiLCJncmV5MjUiKSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKZ2dzYXZlKCdnZW5lcmFsX21pbGxzX2NoYXJ0LnBuZycscGxvdCA9IGdlbmVyYWxfbWlsbHNfY2hhcnQpCgojTWlkZGxlMgptZWRfY2hlX3Byb21vIDwtIGdtX2RhdGEgJT4lIGZpbHRlcihwcm9kdWNlciA9PSAiR2VuZXJhbCBNaWxscyIpICU+JSBncm91cF9ieShicmFuZCxwcm9tbykgJT4lIHN1bW1hcmlzZShtZWRfcmV2ZW51ZSA9IG1lZGlhbihyZXZlbnVlKSwgbiA9IG4oKSkKY2hlZXJpb3NfcHJvbW9fY2hhcnQgPC0gbWVkX2NoZV9wcm9tbyAlPiUgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihicmFuZCwtbWVkX3JldmVudWUpLCB5ID0gbWVkX3JldmVudWUsIGZpbGwgPSBmYWN0b3IocHJvbW8sIGxldmVscyA9IHJldihsZXZlbHMocHJvbW8pKSkpKSArIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCAgcG9zaXRpb24gPSAiZG9kZ2UiKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4ID0gIiIsIHkgPSAiTWVkaWFuIFJldmVudWUiLCB0aXRsZSA9ICJDaGVlcmlvcyBpcyB0aGUgb25seSBHZW5lcmFsIE1pbGxzIGJyYW5kIHdob3NlIFxubWVkaWFuIHJldmVudWUgZGVjcmVhc2VzIHdpdGggcHJvbW90aW9uIixzdWJ0aXRsZSA9IHN1Yl90aXRsZTEpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoJ2dyZXk3NScsJ2dyZXkyNScpLCBuYW1lID0gIiIsIGxhYmVscyA9IGMoIldpdGggUHJvbW8iLCJXaXRob3V0IFByb21vIikpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogICN0aGVtZV9lY29ub21pc3Rfd2hpdGUoZ3JheV9iZyA9RkFMU0UpICsgc2NhbGVfY29sb3VyX2Vjb25vbWlzdCgpKwogIHRoZW1lKGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKQpnZ3NhdmUoJ2NoZWVyaW9zX3Byb21vX2NoYXJ0LnBuZycscGxvdCA9IGNoZWVyaW9zX3Byb21vX2NoYXJ0KQoKI01pZGRsZTMKbWVkX2NoZV9hZCA8LSBnbV9kYXRhICU+JSBmaWx0ZXIocHJvZHVjZXIgPT0gIkdlbmVyYWwgTWlsbHMiKSAlPiUgZ3JvdXBfYnkoYnJhbmQsYWQpICU+JSBzdW1tYXJpc2UobWVkX3JldmVudWUgPSBtZWRpYW4ocmV2ZW51ZSksIG4gPSBuKCkpCmNoZWVyaW9zX2FkX2NoYXJ0IDwtIG1lZF9jaGVfYWQgJT4lIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoYnJhbmQsLW1lZF9yZXZlbnVlKSwgeSA9IG1lZF9yZXZlbnVlLCBmaWxsID0gYWQpKSArIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCAgcG9zaXRpb24gPSAiZG9kZ2UiKSArIAogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4ID0gIiIsIHkgPSAiTWVkaWFuIFJldmVudWUiLCB0aXRsZSA9ICJDaGVlcmlvcyBpcyB0aGUgb25seSBHZW5lcmFsIE1pbGxzIGJyYW5kIHdob3NlIFxubWVkaWFuIHJldmVudWUgZGVjcmVhc2VzIHdpdGggYWRzIixzdWJ0aXRsZSA9IHN1Yl90aXRsZTEpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcikgKwogICNzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdncmV5NzUnLCdncmV5MjUnKSwgbmFtZSA9ICIiLCBsYWJlbHMgPSBjKCJXaXRoIFByb21vIiwiV2l0aG91dCBQcm9tbyIpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICAjdGhlbWVfZWNvbm9taXN0X3doaXRlKGdyYXlfYmcgPUZBTFNFKSArIHNjYWxlX2NvbG91cl9lY29ub21pc3QoKSsKICB0aGVtZShheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkKZ2dzYXZlKCdjaGVlcmlvc19hZF9jaGFydC5wbmcnLHBsb3QgPSBjaGVlcmlvc19hZF9jaGFydCkKCgpgYGAKCg==